目前主流模块规范有:javascript
规范名称 | 运行环境 | 实现 | 加载方式 |
---|---|---|---|
AMD(异步模块定义) | 客户端 | require.js | 异步 |
CMD(通用模块定义) | 客户端 | sea.js | 异步 |
CommonJS | 服务端 | NodeJS | 同步(动态加载) |
es6 | 客户端 | es6 | 静态加载 |
AMD 和 CMD 加载多个文件时都是异步加载html
区别:java
required.js
是 AMD 的实现node
使用:define(id?, dependencies?, factory);
;jquery
// 定义模块 myModule.js define(['dependency'], function(){ var name = 'Byron'; function printName(){ console.log(name); } return { printName: printName }; }); // 加载模块 require(['myModule'], function (my){ my.printName(); });
sea.js
是 CMD 的实现git
CMD 中一个模块就是一个文件es6
使用:define(id?, dependencies?, factory);
;github
// 定义模块 myModule.js define(function(require, exports, module) { var $ = require('jquery.js') $('div').addClass('active'); }); // 加载模块 seajs.use(['myModule.js'], function(my){ });
CommonJS 模块就是对象,输入时必须查找对象属性。express
特色:api
每一个模块内部,都有一个module对象,表明当前模块。它有如下属性。
module.exports = 123
为了方便,Node为每一个模块提供一个 exports
变量,指向 module.exports
。
注意:
// 至关于每一个模块顶部都有这个声明 var exports = module.exports; // 能够给导出的对象添加属性 exports.area = function (r) { return Math.PI * r * r; }; // 不能够导出一个单一值 exports = 123; // 无效
require
命令用于加载模块文件,返回该模块的 exports
对象
ES6 模块不是对象,而是经过 export 命令显式指定输出的代码,再经过 import 命令输入。
优势:
缺点:
经过 export
命令规定模块的对外接口。有两种模式:
// 导出单个 export let name1 = 1; // 导出多个 export { name1, name2, ...}; // 重命名导出 export { name1 as master } // 解构导出并重命名 export const { name1, name2: bar } = o; // 默认导出 export default expression; export { name1 as default, … }; // 聚合模块 - 输入后立马输出 export * from …; // does not set the default export export * as name1 from …; export { name1, name2, …, nameN } from …; export { import1 as name1, import2 as name2, …, nameN } from …; export { default } from …;
经过 import
命令加载模块
import
是静态的,在编译时就加载import
命令会提高到顶部使用:
*
指定一个对象,全部输出值都加载在这个对象上as
能够设置别名import
某个模块,会执行,但不会输入任何值export default
// 执行 lodash 模块,但不输入任何值 import 'lodash'; // 非默认导出需有大括号。可使用 as 设置别名 import { firstName as fn, persion } from './profile.js'; // export default 默认导出不须要括号 import class from './class.js'; // 变量不容许改写 fn = 123; // Syntax Error : 'firstName' is read-only; // 对象属性能够改写 - 不建议 persion.age = 18;
import()
能够实现动态加载
import()
能够像调用函数同样动态导入模块,并返回一个promiseimport()
支持 await
关键字import()
能够在commonJS 模块中运行使用场景:
import('/modules/my-module.js') .then((module) => { // Do something with the module. }); async getModule() { if (true) { let module = await import('/modules/my-module.js'); } }
"循环加载"(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。
// a.js var b = require('b'); // b.js var a = require('a');
一般,"循环加载"表示存在强耦合,若是处理很差,还可能致使递归加载,使得程序没法执行。
如何解决?commonJS 和 Es6 的module 给出了不一样的解决方案。
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);
执行main.js
node main.js 在 b.js 之中,a.done = false b.js 执行完毕 在 a.js 之中,b.done = true a.js 执行完毕 在 main.js 之中, a.done=true, b.done=true
ES6 是静态加载,不会缓存结果。 它只是生成一个指向被加载模块的引用,每次都会根据引用动态去加载模块取值。
// a.mjs import {bar} from './b'; console.log('a.mjs'); console.log(bar); export let foo = 'foo'; // b.mjs import {foo} from './a'; console.log('b.mjs'); console.log(foo); export let bar = 'bar';
执行
node --experimental-modules a.mjs b.mjs ReferenceError: foo is not defined -> 缘由: foo未定义。
解决:将foo写成函数,使它产生函数提高。函数表达式不行,由于它不能提高
// a.mjs import {bar} from './b'; console.log('a.mjs'); console.log(bar()); function foo() { return 'foo' } export {foo}; // b.mjs import {foo} from './a'; console.log('b.mjs'); console.log(foo()); function bar() { return 'bar' } export {bar};
执行
$ node --experimental-modules a.mjs b.mjs foo a.mjs bar
例子2:
// even.js import { odd } from './odd' export var counter = 0; export function even(n) { counter++; return n === 0 || odd(n - 1); } // odd.js import { even } from './even'; export function odd(n) { return n !== 0 && even(n - 1); }
执行
$ babel-node > import * as m from './even.js'; > m.even(10); true > m.counter 6 > m.even(20) true > m.counter 17
m.even(20) 执行的时候,此时 counter 已经等于 6,加上本身执行的 11 次,因此一共是 17 次