- 概述
- 使用方法和注意点
一直以来 javascript 的模块化要么使用 commonJS
,要么使用 AMD
,在 ES6 中开始支持模块化了,关键字主要有 import
(导入)和 export
(导出)。最近一段时间一直在思考 ES6 中的模块化与我们正常使用的 AMD
( 如 requireJS
),以及 commomJS
(如 nodeJS
)有何不同。在搜索了相关的资料后,发现 ES6 模块化中有些地方很值得注意。
ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。 CommonJS
和 AMD
模块,都只能在运行时确定这些东西。
import
和 export
命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。以下的写法会报错
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
// 报错
function foo() {
export default 'bar' // SyntaxError
}
foo()
// 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
上面三种写法都会报错,因为它们用到了表达式、变量和 if
结构。在静态分析阶段,这些语法都是没法得到值的。
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
// main.js
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
注意,模块整体加载所在的那个对象(上例是 circle
),应该是可以静态分析的,所以不允许运行时改变。下面的写法都是不允许的。
//main.js
import * as circle from './circle';
// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {};
// 报错
export 1;
// 报错
var m = 1;
export m;
上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出1,第二种写法通过变量m,还是直接输出1。1只是一个值,不是接口。正确的写法是下面这样。
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
上面三种写法都是正确的,规定了对外的接口m。其他脚本可以通过这个接口,取到值1。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。
2.3.2 export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。这一点与 CommonJS
规范完全不同。 CommonJS
模块输出的是值的缓存,不存在动态更新。
// jq.js
function myfun () {
console.log('excuted');
}
export {
myfun
}
引入方式:
import {myfun} from 'js/jq';
//执行
myfun();
// jq.js
function myfun () {
console.log('excuted');
}
export {
myfun
}
引入方式:
import * as fn from 'js/jq';
//执行
fn.myfun();
// jq.js
function myfun () {
console.log('excuted');
}
export default {
myfun
}
引入方式:
import fn from 'js/jq';
//执行
fn.myfun();