import和require的区别

require 和 import,都是为了JS模块化使用。不知道有没有小伙伴和我一样,最开始有点分不清楚import和require的区别,今天就给大家梳理一下他们之间的区别。由于有时候多人协同开发时,会出现一个项目中同时使用了import和require引入的情况。在正常的情况下,一个项目中最好使对引入方式做一个规范。

1. 遵循规范的区别

  1. import/export是ES6新规范
  2. require/exports是CommonJS的一部分

ES6标准发布后,module成为标准,标准的使用是以export指令导出接口,以import引入模块;但是在node模块中,我们采用的是CommonJS规范,使用require引入模块,使用module.exports导出接口。

2. 出现时间的区别

require/exports 出生在野生规范当中,什么叫做野生规范?即这些规范是 JavaScript 社区中的开发者自己草拟的规则,得到了大家的承认或者广泛的应用。比如 CommonJS、AMD、CMD 等等。import/export 则是名门正派。

CommonJS 作为 Node.js 的规范,一直沿用至今。(就是因为CommonJS填补了JS没有模块的功能空缺,它的目标是让JS不止在浏览器中运行,可以在各个地方都能运行。)由于CommonJS 和 ES6 之间的差异,所以Node.js 无法直接兼容 ES6。因此我们在现阶段掌握require/exports是必要的。

ES6在语言规格的层面上,实现了模块功能,而且实现得相当简单,完全可以取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。

3. require

require是Commonjs的规范,node应用是由模块组成的,遵从Commonjs的规范即exports是模块公开的接口,require用于从外部获取一个模块接口,举一个例子:
x.js与y.js是同级文件。
在x.js文件中写入代码:

1
2
3
4
5
6
function demo(args) {
// content...
console.log(args);
}

module.exports = { demo };

y.js文件中写入代码:

1
2
let { demo } = require("./x.js");
demo("I am x_demo!!!");

通过在终端运行:node y.js可以运行出:I am x_demo!!!

require的使用:在导出的文件中定义module.exports,导出的对象类型不予限定(可为任意类型)。在导入的文件中使用require()引入即可使用。本质上,是将要导出的对象,赋值给module这个对象的exports属性,在其他文件中通过require这个方法来访问exports这个属性。

4. import

import是ES6为JS模块化提出的新的语法,import(导入)要与export(导出)结合使用,举一个例子。
index.html文件与x.js与y.js是同级文件。
在index.html文件中写入代码:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="module" src="./y.js"></script>
</body>
</html>

在x.js文件中写入代码:

1
2
3
4
5
6
7
export function demo(args) {
// content...
console.log(args);
}
export default function() {console.log("I am default!!!")}; // 默认导出模块,一个文件中只能定义一个
export const age = "22";
export const name = "daipi173";

y.js文件中写入代码:

1
2
3
4
5
6
import def, { demo, age, name } from './x.js';  // def代表引入的export default的内容
demo(`My name is ${name},and I am ${age} years`);
def();
// 控制台打印内容:
// My name is daipi173,and I am 22 years
// I am default!!!

注意:如果不放在服务器下面会有一个报错信息,如下图所示:
不放在服务器下的报错信息
此时的解决办法就是将它放在服务器下面就可以解决相应的问题。我们可以下一个服务器,Windows系统的可以下一个wamp,Mac系统的可以下一个mamp。

5. as关键字

as简单的说就是取一个别名。export中可以用,import中也可以用。下面我们看一个例子。
a.js文件与b.js文件与c.js文件是同级文件:
a.js文件代码如下:

1
2
var a = function() {};
export {a as fun};

b.js文件代码如下:

1
2
import {fun as a} from './a';
a();

上面这段代码,export的时候,对外提供的接口是fun,它是a.js内部a这个函数的别名,但是在模块外面,不认识a,只认识fun。
import中的as就很简单,就是你在使用模块里面的方法的时候,给这个方法取一个别名,好在当前的文件里面使用。之所以是这样,是因为有的时候不同的两个模块可能通过相同的接口,比如有一个c.js也通过了fun这个接口。
c.js文件代码如下:

1
export function fun() {};

如果在b.js中同时使用a和c这两个模块,就必须想办法解决接口重名的问题,as就解决了。

6. default关键字

在export的时候,可能会用到default,说简单一些它其实是别名的语法糖。
在x.js文件中写入代码:

1
2
3
4
5
export default function() {}

// 等价于
function test() {};
export {test as default};

在import的时候,可以这样使用:

1
2
3
import a from './x';
// 等价于或者说就是下面这种写法的简写,是同一个意思
import {default as a} from './x';

注意:这个语法糖的好处就是import的时候,可以省去花括号{}。简单的说,如果import的时候,你发现某个变量没有花括号括起来(没有*号),那么你在脑海中应该把它还原成有花括号的as语法。就如上面代码所示。

7. *符号

就是代表所有,只用在import中。
*
注意:符号尽可能少用,它实际上是使用所有export的接口,但是很有可能你的当前模块并不会用到所有接口,可能仅仅是一个,所以最好的建议是使用花括号,用一个加一个。*

8. 形式的区别

  1. ES6中import/export的写法比较多,下面我们来举一些例子看看:
    1
    2
    3
    4
    5
    6
    7
    8
    // 导入写法
    import dai from "dai";
    import {default as dai} from "dai";
    import * as dai from "dai";
    // 导出写法
    export default dai;
    export const dai;
    export * from "dai";
  2. CommonJS中require/exports的用法只有下面几种写法:
    1
    2
    3
    const dai = require("dai");
    exports.dai = dai;
    module.exports = dai;

9. Commonjs模块与ES6模块的区别

  1. commonjs输出的是一个值的拷贝,而es6输出的是值的引用
  2. commonjs是运行时加载,es6是编译时输出接口。

10. require和import的使用场合

require的使用场合:
require的使用非常简单,它相当于module.exports的传送门,module.exports后面的内容是什么,require的结果就是什么,对象、数字、字符串、函数……再把require的结果赋值给某个变量,相当于把require和module.exports进行平行空间的位置重叠。

require理论上可以运用在代码的任何地方,甚至不需要赋值给某个变量之后再使用,而且你在使用时,完全可以忽略模块化这个概念来使用require,仅仅把它当做一个node内置的全局函数,它的参数甚至可以是表达式:。举一个例子,代码如下所示:

1
2
3
4
require('./a')(); // a模块是一个函数,立即执行a模块函数
var data = require('./a').data; // a模块导出的是一个对象
var a = require('./a')[0]; // a模块导出的是一个数组
require(process.cwd() + '/a'); // 可以作为表达式

import使用场合:
import是编译时的,它必须放在文件开头,而且使用格式也是确定的,它不会将整个模块运行后赋值给某个变量,而是只选择import的接口进行编译,这样在性能上比require好很多。

如果还有不懂或者想要深入学习的同学,可以去网上搜索相关的内容进行一个补充。