ES模块化
特点:
-
用于服务端和浏览器
-
输出的是值的引用
-
import命令是异步加载 -
静态编译:从fs模块加载三个方法,其他方法不加载,在编译时就完成模块加载
// ES6模块 import { stat, exists, readFile } from 'fs'; -
自动采用严格模式(不管是否加上“use strict”)
-
ES6模块中顶层的this指向undefined
export和import
// script1.js
export const A = 1;
export const B = 3;
export const C = 4;
// 等价于
// const A = 1;
// const B = 3;
// const C = 4;
// export {
// A,
// B,
// C
// }
// script2.js
import * as constants from './script1.js';
console.log(constants.A); // 1
console.log(constants.B); // 3
// 等价于
// import {A, B} from './script1.js';
// console.log(A);
// console.log(B)
注意点
-
export和import命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错 -
import命令输入的变量都是只读的,不允许在加载模块的脚本里改写接口,但可以改写加载对象的属性 -
import命令具有提升效果,会提升到整个模块的头部,首先执行这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。以下代码不会报错:foo(); import { foo } from 'my_module'; -
多次执行同一个import会合并,只执行一遍
import {A} from './script1.js'; import {B} from './script1.js'; // 等同于 import {A, B} from './script1.js';
export default
- export导入时需要知道变量名,export default表示将导出的变量赋值给名为default的变量,导入时不需要知道变量名,可以随意命名,不需要使用{}解构赋值
- 一个模块只能有一个export default
- export default后面不能跟着声明语句:export default var a = 100会报错,export default 100可以
// script1.js
let a = 100;
export default a; // 导出一个值与a相同,名字叫做default的变量
// script2.js
import A from './script1.js'
console.log(A)
import ()
- 支持动态加载
- 返回一个Promise,加载模块成功以后,这个模块会作为一个对象,当作
then方法的参数 import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载- 使用场合:
- 按需加载
- 条件加载
- 动态的模块路径
import('./testMoudle.js')
.then(({export1, export2}) => {
// ...·
});
加载规则
- 浏览器加载ES6模块,也使用script标签,但是加入type="module"属性,正常sciprt标签是type="application/javascript"可以省略
- 浏览器对于带有
type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性
ES模块与CommonJS区别:
- CommonJS 模块输出的是一个值的拷贝(被缓存),ES6 模块输出的是值的引用
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
- CommonJS 模块的
require()是同步加载模块,ES6 模块的import命令是异步加载 - ES6 模块之中,顶层的
this指向undefined;CommonJS 模块的顶层this指向当前模块,这是两者的一个重大差异
CommonJS规范
-
主要用于服务器
-
输出的是一个值的拷贝
-
require()是同步加载模块 -
CommonJS规范是node专用,
.mjs文件总是以 ES6 模块加载,.cjs文件总是以 CommonJS 模块加载,.js文件的加载取决于package.json里面type字段的设置 -
运行时加载:下面代码先整体加载fs模块,生成一个对象(_fs),然后再从这个对象上读取三个方法
// CommonJS模块 let { stat, exists, readfile } = require('fs'); // 等同于 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile; -
Module构造函数
module.id模块的识别符,通常是带有绝对路径的模块文件名module.filename模块的文件名,带有绝对路径module.loaded返回一个布尔值,表示模块是否已经完成加载module.parent返回一个对象,表示调用该模块的模块module.children返回一个数组,表示该模块要用到的其他模块module.exports表示模块对外输出的值
function Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; // ... } -
CommonJS导出的值会被缓存
// script1.js var num = 1; function change() { num++; } module.exports = { num, change };// script2.js var { num } = require('./script1.js') var { change } = require('./script2.js') console.log(num) // 1 change() console.log(num) // 1 -
exports变量:指向module.exports变量,不能直接修改exports指向
exports.num = num exports.change = change // 报错,将exports指向对向,切断了与module.exports联系 exports = { num, change } -
循环加载:CommonJS 模块遇到循环加载时,返回的是当前已经执行的部分的值,而不是代码全部执行后的值
// a.js exports.done = false; var b = require('./b.js'); console.log('在 a.js 之中,b.done = %j', b.done); exports.done = true; console.log('a.js 执行完毕');// b.js exports.done = false; var a = require('./a.js'); console.log('在 b.js 之中,a.done = %j', a.done); exports.done = true; console.log('b.js 执行完毕');// main.js var a = require('./a.js'); var b = require('./b.js'); console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done); // 输出 在 b.js 之中,a.done = false b.js 执行完毕 在 a.js 之中,b.done = true a.js 执行完毕 在 main.js 之中, a.done=true, b.done=true
AMD规范
- 主要用于浏览器
- 异步加载
参考es6.ruanyifeng.com/#docs/modul…