ES6 的模块加载(import)和 CommonJS(require)有两大区别:
- CommonJS 输出值的拷贝,ES6 输出值得引用
ES6 模块输出的是引用,类似 Unix 的 “符号链接”,import 的值不会缓存,每次都会去模块读值,也因此不能重新赋值。
- CommonJS 是运行时加载,ES6 是编译时加载
CommonJS 加载的是对象,只有在运行时才会生成;而 ES6 接受的是静态的定义,编译时就可以完成。
ES6 的模块是在编译时被加载的,缺点是无法实现条件加载,但优点是静态分析速度快。CommonJS 是运行时加载,可以做到条件加载,所以比较适用于后端也就是 Node 的开发环境。
比如 ES6 的 import,下面的写法不会报错:
foo();
import { foo } from 'my_module';
因为 import 是在编译时(静态)执行的,在代码运行之前。也因此,不能用表达式和变量来执行 import,比如:
if (x === 1){
import { foo } from 'my_module1';
} else {
import { foo } from 'my_module2';
}
以上代码会报语法错误。
====
ES6 的模块加载用的是 import 方法,而 node 用的是 CommonJS 规范的 require
Node 采用 CommonJS 规范加载模块,因为 Node 的主要应用场景在后端(服务器),所有文件都保存在本地,而 CommonJS 又是运行时(同步)加载,所以读取快,这样做比较合理。但是在前端(浏览器),更合理的方式是采用 ES6 的编译时(异步)加载。
===
此外,CommonJS 在加载时(require)会执行整个脚本,然后输出一个对象,之后再遇到 require 就直接返回第一次的结果而不会执行,除非手动清理系统缓存。
ES6 在用 import 加载模块时也会执行,多次 import 只会执行一次
ES6 的 import
导出指定变量的时候如下:
--- a.js ---
export var a = 1
--- b.js ---
import { a } from '.a'
若不指定变量:(本质上是把后面的值赋给 default 变量,所以 export default var a = 1 是不合法的)
--- a.js ---
export default 1
--- b.js ---
import a from '.a'
用 export default 的时候,import 可以不用加 {},名字也可以自定义。default 也可以和其它具名变量一起导入:
--- a.js ---
export default function (obj) {
// ···
}
export function each(obj, iterator, context) {
// ···
}
export { each as forEach };
--- b.js ---
import _, { each, forEach } from 'a';
也可以把模块当做入口,直接导出另一个模块:
export { foo, bar } from 'my_module';
// 接口改名
export { foo as myFoo } from 'my_module';
// 整体导出,注意 * 会忽略 default 方法
export * from 'my_module';
// 导出默认
export { default } from 'foo';
// 具名接口改默认
export { es6 as default } from './someModule';
// 默认接口改具名
export { default as es6 } from './someModule';
当用 import 加载 CommonJS 时,module.export 会被当做 export default 默认输出