上一篇介绍了CommonJS的模块规范, 这一篇主要谈一下ES6的模块
- 模块需要严格模式
- 一个模块就是一个独立的文件,该文件内部的所有变量外部是无法获取的。
- 导出:使用export声明需要导出的变量、函数或类
- 导入:使用import声明从其他模块导入变量、函数或类。
export 命名输出(Named exports)
1). 写法1, 在每个变量、函数或类前面加上关键字export。1
2
3
4
5
6
7
8
9
10
11
12
13// lib.ts export导出
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function add(x, y) {
return x + y;
}
// feature.ts, 在这个文件中import导入
import {sqrt, square} from 'lib';
console.log(square(8));
console.log(add(10,15));
2). 写法2, export列表, 每个要输出的部分前都写上 export 很麻烦,你可以只写一行你想要输出的变量列表,再用花括号包起来。1
2
3
4
5
6
7
8
9
10
11
12
13
14// lib.ts export导出的另外一个写法(推荐)
const sqrt = Math.sqrt;
function square(x) {
return x * x;
}
function add(x, y) {
return x + y;
}
export {sqrt, square, add};
// feature.ts, 在这个文件中import导入
import {sqrt, square} from 'lib';
console.log(square(8));
console.log(add(10,15));
import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(lib.ts)对外接口的名称相同。
注意 import { sqrt, square } from ‘lib’; 是import的特有语法,并不是 析构(destructing) 语法
如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。
export default默认输出
上面的导出写法比较麻烦, 必须要指定一个名字。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。。1
2
3
4
5
6
7
8// add.ts
export default function (x,y){
return x + y;
}
// feature.ts
import add from './add'
add(10,9);
默认输出的时候不需要指定一个变量名,它默认就是文件名。加载该模块时,import命令可以为该匿名函数指定任意名字。需要注意的是,这时import命令后面,不使用大括号。
本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。它后面不能跟变量声明语句。1
2
3
4
5// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
模块的整体加载 *
除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象里1
2
3
4
5
6
7
8
9
10
11
12
13
14// lib.ts
const sqrt = Math.sqrt;
function square(x) {
return x * x;
}
function add(x, y) {
return x + y;
}
export {sqrt, square, add};
// feature.ts
import * as lib from 'lib';
console.log(lib.square(8));
console.log(lib.add(10,15));
重命名模块输入和模块输出 as
如果导入的变量名恰好和你模块中的变量名冲突了,ES6 允许你给你导入的东西重命名:1
2import {add as addNum} from "./add";
addNum(10,9);
类似地,你在导出变量的时候也能重命名。这个特性在你想将同一个变量名导出两次的场景下十分方便,举个栗子:1
2
3
4
5function add() {}
export {
add as addNum,
add as addInt
};
有趣的静态和动态
JavaScript作为一门动态语言已经得到了一个令人惊讶的静态模块系统。
- 你只可以在模块的最外层作用域使用import和export,不可在条件语句中使用,也不能在函数作用域中使用import。
- 所有导出的标识符一定要在源代码中明确地导出它们的名称,你不能通过编写代码遍历一个数组然后用数据驱动的方式导出一堆名称。
- 模块对象被冻结了,所以你无法hack模块对象并为其添加polyfill风格的新特性。
- 一个模块的所有依赖必须在模块代码运行前完全加载、解析并且及早连接,不存在一种通过import来按需懒加载的语法。
- import模块产生的错误没有错误恢复机制。一个app可能囊括了上百个模块,一旦有一个模块无法加载或连接,所有的模块都不会运行,而且你不能在try/catch代码块中捕捉import的错误信息。(上面这些描述的本意是说:系统是静态的,webpack可在编译时为你检测那些错误。)
- 不支持在模块加载依赖前运行其它代码的钩子,这也意味着无法控制模块的依赖加载过程。
Reference
- ES6 in Depth (关于 ES6 的一系列新特性的介绍。)
- 深入浅出ES6(十六):模块 Modules