一直以来javascript的模块化要么使用commonJS,要么使用AMD,在ES6中开始支持模块化了,关键字主要有import(导入)和export(导出)。最近一段时间一直在思考ES6中的模块化与我们正常使用的AMD(如requireJS),以及commomJS(如nodeJS)有何不同。在搜索了相关的资料后,发现ES6模块化中有些地方很值得注意。
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
1.由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
// 报错
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结构。在静态分析阶段,这些语法都是没法得到值的。
2.import语句会执行所加载的模块.如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。
3.模块的整体加载
// 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.export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 报错
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.export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。这一点与 CommonJS 规范完全不同。CommonJS 模块输出的是值的缓存,不存在动态更新。
export 和 import 使用的例子
1.export {} + import {}
// jq.js
function myfun () {
console.log('excuted');
}
export {
myfun
}
引入方式:
import {myfun} from 'js/jq';
//执行
myfun();
2.export {} + import * as xxx
// jq.js
function myfun () {
console.log('excuted');
}
export {
myfun
}
引入方式:
import * as fn from 'js/jq';
//执行
fn.myfun();
3.import default + export xxx
// jq.js
function myfun () {
console.log('excuted');
}
export default {
myfun
}
引入方式:
import fn from 'js/jq';
//执行
fn.myfun();