目前主流模塊規範有: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 次