一次性搞懂 CommonJS, AMD, CMD , ES Module 等模塊化規(guī)范

1 常見的模塊化規(guī)范

  • CommonJs (Node.js)
  • AMD (RequireJS)
  • CMD (SeaJS)
  • ES Module(ES6 模塊)

2 模塊化的優(yōu)點(diǎn)

  • 在模塊化開發(fā)中词渤,通常一個文件就是一個模塊疟暖。該文件有自己的作用域,只向外暴露特定的變量和函數(shù)撵孤,并且可以按需加載。
  • 依賴自動加載呕缭,按需加載埠戳。
  • 提高代碼復(fù)用率,方便進(jìn)行代碼的管理贡歧,使得代碼管理更加清晰、規(guī)范赋秀。
  • 減少了命名沖突利朵,消除全局變量。
  • 目前流行的 js 模塊化規(guī)范有 CommonJS猎莲、AMD绍弟、CMD 以及 ES6 的模塊系統(tǒng)。

3 CommonJS(Node.js)同步加載模塊

CommonJS 是服務(wù)器模塊的規(guī)范著洼,Node.js采用了這個規(guī)范樟遣。

根據(jù) CommonJS 規(guī)范,一個單獨(dú)的文件就是一個模塊身笤,每個模塊都是一個單獨(dú)的作用域豹悬,每個文件中定義的變量(還包括函數(shù)和類),都是私有的液荸,對其他文件是不可見的瞻佛。

CommonJS 規(guī)范加載模塊是同步的,也就是說莹弊,只有加載完成涤久,才能執(zhí)行后面的操作涡尘。

CommonJS 中忍弛,加載模塊要使用 require 方法。該方法讀取一個文件并執(zhí)行考抄,最后返回文件內(nèi)部的 exports 對象细疚。

math.js

var x = 5;
var addX = function(value) {
  return value + x;
};

module.exports.x = x;
module.exports.addX = addX;

// 也可以改寫為如下
module.exports = {
  x: x,
  addX: addX,
};

main.js

let math = require('./math.js');
console.log('math.x',math.x);
console.log('math.addX', math.addX(4));

Node.js 主要用于服務(wù)器編程,加載的模塊文件一般都已經(jīng)存在于本地硬盤中川梅,加載起來較快疯兼,不用考慮異步加載的方式然遏,故 CommonJS 的同步加載模塊規(guī)范是比較適用的。

但若在瀏覽器環(huán)境中吧彪,要從服務(wù)器加載模塊待侵,這時就必須采用異步加載模式,故有了 AMD姨裸,CMD 等解決方案秧倾。

4 AMD(RequireJS)異步加載模塊

  • AMD = Asynchronous Module Definition,即 異步模塊定義傀缩。
  • AMD 規(guī)范加載模塊是異步的那先,并允許函數(shù)回調(diào)。不必等到所有模塊都加載完成赡艰,后續(xù)操作也可正常執(zhí)行售淡。
  • AMD 中,使用 require 獲取依賴模塊慷垮,使用 exports 導(dǎo)出 API揖闸。
// 規(guī)范 API
define(id?, dependencies?, factory);
define.amd = {};

// 定義無依賴的模塊
define({
    add: function(x,y){
        return x + y;
    }
});

// 定義有依賴的模塊
define(["alpha"], function(alpha){
    return {
        verb: function(){
            return alpha.verb() + 1;
        }
    }
});

異步加載和回調(diào)

require([module], callback)callback 為模塊加載完成后的回調(diào)函數(shù)。

// 加載 math模塊料身,完成之后執(zhí)行回調(diào)函數(shù)
require(['math'], function(math) {
  math.add(2, 3);
});

RequireJS

RequireJS 是一個 前端模塊化管理 的工具庫楔壤,遵循 AMD 規(guī)范,RequireJS 是對 AMD 規(guī)范的闡述惯驼。

RequireJS 的基本思想為蹲嚣,通過一個函數(shù)將所需或所依賴的模塊全部裝載進(jìn)來,然后返回一個新的函數(shù)(模塊)祟牲。后續(xù)所有關(guān)于新模塊的業(yè)務(wù)代碼隙畜,都在這個函數(shù)內(nèi)部操作。

RequireJS 要求每個模塊均放在獨(dú)立的文件之中说贝,并使用 define 定義模塊议惰,使用 require 方法調(diào)用模塊。

根據(jù)是否有依賴其他模塊的情況乡恕,可分為 獨(dú)立模塊非獨(dú)立模塊言询。

獨(dú)立模塊(不依賴其他模塊,直接定義)

define({
    method1: function(){},
    method2: function(){}
});

// 等價(jià)于
define(function() {
    return {
        method1: function(){},
        method2: function(){}
    }
});

非獨(dú)立模塊(依賴其他模塊)

define([ 'module1', 'module2' ], function(m1, m2) {
    ...
});

// 等價(jià)于
define(function(require) {
    var m1 = require('module1');
    var m2 = require('module2');
    ...
});

調(diào)用模塊(使用 require 方法)

require(['foo', 'bar'], function(foo, bar) {
    foo.func();
    bar.func();
});

5 CMD(SeaJS)異步模塊

CMD 的定義

  • CMD = Common Module Definition傲宜,即 通用模塊定義运杭。CMDSeaJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
  • CMD 規(guī)范和 AMD 規(guī)范類似函卒,都主要運(yùn)行于瀏覽器端辆憔,寫法上看起來也很類似。主要區(qū)別,在于 模塊初始化時機(jī)虱咧。

AMD 與 CMD 的異同

  • AMD 中熊榛,只要模塊作為依賴時就會加載并進(jìn)行初始化。
  • CMD 中腕巡,模塊作為依賴且被引用時才會初始化玄坦,否則只會加載。
  • CMD 推崇依賴就近绘沉,可以把依賴寫進(jìn)你的代碼中的任意一行营搅。AMD 推崇依賴前置。
  • AMDAPI 默認(rèn)是一個當(dāng)多個用梆砸,CMD 嚴(yán)格的區(qū)分推崇職責(zé)單一转质。例如:AMDrequire 分全局的和局部的。CMD里面沒有全局的 require帖世,提供 seajs.use() 來實(shí)現(xiàn)模塊系統(tǒng)的加載啟動休蟹。CMD 里每個 API 都簡單純粹。
// AMD
define(['./a','./b'], function (a, b) {

    // 依賴一開始就寫好
    a.test();
    b.test();
});

// CMD
define(function (require, exports, module) {

    // 依賴可以就近書寫
    var a = require('./a');
    a.test();

    ...
    // 軟依賴
    if (status) {
        var b = require('./b');
        b.test();
    }
});

Sea.js

使用 Sea.js日矫,在書寫文件時需要遵守 CMD(Common Module Definition)模塊定義規(guī)范赂弓,一個文件就是一個模塊。

用法
  • 通過 exports 暴露接口哪轿。這意味著不需要命名空間了盈魁,更不需要全局變量。這是一種徹底的命名沖突解決方案窃诉。
  • 通過 require 引入依賴杨耙。這可以讓依賴內(nèi)置,開發(fā)者只需關(guān)心當(dāng)前模塊的依賴飘痛,其他事情 Sea.js 都會自動處理好珊膜。對模塊開發(fā)者來說,這是一種很好的 關(guān)注度分離宣脉,能讓程序員更多地享受編碼的樂趣车柠。
  • 通過 define 定義模塊。
示例

例如塑猖,對于下述 util.js 代碼

var org = {};
org.CoolSite = {};
org.CoolSite.Utils = {};

org.CoolSite.Utils.each = function (arr) {
  // 實(shí)現(xiàn)代碼
};

org.CoolSite.Utils.log = function (str) {
  // 實(shí)現(xiàn)代碼
};

可以采用 SeaJS 重寫為


define(function(require, exports) {
  exports.each = function (arr) {
    // 實(shí)現(xiàn)代碼
  };

  exports.log = function (str) {
    // 實(shí)現(xiàn)代碼
  };
});

通過 exports 就可以向外提供接口竹祷。通過 require('./util.js') 就可以拿到 util.js 中通過 exports 暴露的接口。這里的 require 可以認(rèn)為是 Sea.js 給 JavaScript 語言增加的一個語法關(guān)鍵字羊苟,通過 require 可以獲取其他模塊提供的接口塑陵。

define(function(require, exports) {
  var util = require('./util.js');
  exports.init = function() {
    // 實(shí)現(xiàn)代碼
  };
});

SeaJS 與 RequireJS 區(qū)別

二者區(qū)別主要表現(xiàn)在 模塊初始化時機(jī)

  • AMD(RequireJS)中只要模塊作為依賴時,就會加載并初始化践险。即盡早地執(zhí)行(依賴)模塊猿妈。相當(dāng)于所有的 require 都被提前了,而且模塊的執(zhí)行的順序也不一定就是 require 的書寫順序巍虫。

  • CMD(SeaJS)中彭则,模塊作為依賴且被引用時才會初始化,否則只會加載占遥。即只會在模塊真正需要使用的時候才初始化俯抖。模塊加載的順序是嚴(yán)格按照 require 書寫的順序來的。

規(guī)范 生態(tài) 書寫 易于實(shí)現(xiàn) NodeJS 模塊相似性
AMD ★★★★★ ★★★ ★★★★★ ★★★
CMD ★★ ★★★★★ ★★★ ★★★★★

從規(guī)范上來說瓦胎,AMD 更加簡單且嚴(yán)謹(jǐn)芬萍,適用性更廣,而在 RequireJS 強(qiáng)力的推動下搔啊,在國外幾乎成了事實(shí)上的異步模塊標(biāo)準(zhǔn)柬祠,各大類庫也相繼支持 AMD 規(guī)范。

但從 SeaJS 與 CMD 來說负芋,也做了很多不錯東西:1. 相對自然的依賴聲明風(fēng)格 2. 小而美的內(nèi)部實(shí)現(xiàn) 3. 貼心的外圍功能設(shè)計(jì) 4. 更好的中文社區(qū)支持漫蛔。

6 UMD

  • UMD = Universal Module Definition,即 通用模塊定義旧蛾,它是AMDCommonJS的糅合莽龟。
  • AMD 模塊以 瀏覽器第一的原則 發(fā)展,選擇異步加載锨天。CommonJS 模塊以 服務(wù)器第一原則 發(fā)展毯盈,選擇同步加載。由此病袄,迫使人們又想出另一個更通用的模式 UMD(Universal Module Definition)搂赋,實(shí)現(xiàn)跨平臺的解決方案。
  • UMD 先判斷支持 Node.js 的模塊(exports)是否存在益缠,存在則使用 Node.js 模塊模式厂镇。再判斷支持 AMDdefine )是否存在,存在則使用 AMD 方式加載模塊左刽。
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.utilName = factory());
}(this, function () {
    //module ...
});

7 ES Module(ES6 模塊)

CommonJS 和 ES6 模塊 的區(qū)別

  • CommonJS 模塊是 運(yùn)行時加載捺信,ES6 模塊是 編譯時輸出接口。
  • ES6 模塊 輸出的是值的引用欠痴,輸出接口動態(tài)綁定迄靠,而 CommonJS 輸出的是值的拷貝。
CommonJS 輸出值的拷貝

CommonJS 模塊輸出的是值的拷貝喇辽,導(dǎo)出值改變不會導(dǎo)致導(dǎo)入值改變掌挚。

lib.js

var counter = 3;
var obj = {
    name: 'David'
};

function changeValue() {
    counter++;
    obj.name = 'Peter';
};

module.exports = {
    counter: counter,
    obj: obj,
    changeValue: changeValue,
};

main.js

var mod = require('./lib');

console.log(mod.counter);  // 3
console.log(mod.obj.name);  //  'David'
mod.changeValue();

console.log(mod.counter);  // 3
console.log(mod.obj.name);  //  'Peter'
// But
console.log(require('./lib').counter);  // 3
console.log(require('./lib').obj.name);  //  'Peter'
  • counter 是基本類型值,模塊內(nèi)部值的變化 不影響 輸出的值變化菩咨。
  • obj 是引用類型值吠式,模塊內(nèi)部值的變化 會影響 輸出的值變化陡厘。
  • 上述兩點(diǎn)區(qū)別,可類比基本類型和引用類型的賦值操作特占。

也可以借助取值函數(shù)(getter)糙置,將 counter 轉(zhuǎn)為引用類型值,效果如下是目。

在類的內(nèi)部谤饭,可以使用 getset 關(guān)鍵字,對某個屬性設(shè)置存值函數(shù)和取值函數(shù)懊纳,攔截該屬性的存取行為揉抵。 —— class | 阮一峰

lib.js

var counter = 3;
function incCounter() {
  counter++;
}

module.exports = {
  get counter() {
    return counter
  },
  incCounter: incCounter,
};

main.js

var mod = require('./lib');

console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 4
ES6 輸出值的引用

ES6 模塊是動態(tài)關(guān)聯(lián)模塊中的值,輸出的是值的引用嗤疯。原始值變了冤今,import 加載的值也會跟著變。

ES6 模塊的運(yùn)行機(jī)制與 CommonJS 不一樣茂缚。JS 引擎對腳本靜態(tài)分析時辟汰,遇到模塊加載命令 import,就會生成一個只讀引用阱佛。等到腳本真正執(zhí)行時帖汞,再根據(jù)這個只讀引用,到被加載的那個模塊里面去取值凑术。ES6 模塊中翩蘸,原始值變了,import 加載的值也會跟著變淮逊。因此催首,ES6 模塊是動態(tài)引用,并且不會緩存值泄鹏。 —— ES6 Module 的加載實(shí)現(xiàn) | 阮一峰

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4
CommonJS 運(yùn)行時加載 ES6靜態(tài)編譯

CommonJS 模塊是運(yùn)行時加載郎任,ES6 模塊是編譯時輸出接口。

這是因?yàn)椋?strong>CommonJS 加載的是一個對象(即 module.exports 屬性)备籽,該對象只有在腳本運(yùn)行完才會生成舶治。而 ES6 模塊不是對象,它的對外接口只是一種靜態(tài)定義车猬,在代碼靜態(tài)解析階段就會生成霉猛。

ES6 模塊是編譯時輸出接口,因此有如下2個特點(diǎn)

  • import 命令會被 JS 引擎靜態(tài)分析珠闰,優(yōu)先于模塊內(nèi)的其他內(nèi)容執(zhí)行
  • export 命令會有變量聲明提升的效果
import 優(yōu)先執(zhí)行

在文件中的任何位置引入 import 模塊都會被提前到文件頂部

// a.js
console.log('a.js')
import { foo } from './b';

// b.js
export let foo = 1;
console.log('b.js 先執(zhí)行');

// 執(zhí)行結(jié)果:
// b.js 先執(zhí)行
// a.js

雖然 a 模塊中 import 引入晚于 console.log('a')惜浅,但是它被 JS 引擎通過靜態(tài)分析,提到模塊執(zhí)行的最前面伏嗜,優(yōu)于模塊中的其他部分的執(zhí)行坛悉。

export 命令變量提升效果

由于 importexport 是靜態(tài)執(zhí)行伐厌,所以 importexport 具有變量提升效果。即 importexport 命令在模塊中的位置并不影響程序的輸出裸影。

// a.js
import { foo } from './b';
console.log('a.js');
export const bar = 1;
export const bar2 = () => {
  console.log('bar2');
}
export function bar3() {
  console.log('bar3');
}

// b.js
export let foo = 1;
import * as a from './a';
console.log(a);

// 執(zhí)行結(jié)果:
// { bar: undefined, bar2: undefined, bar3: [Function: bar3] }
// a.js

a 模塊引用了 b 模塊挣轨,b 模塊也引用了 a 模塊,export 聲明的變量也是優(yōu)于模塊其它內(nèi)容的執(zhí)行的空民。但具體對變量賦值需要等到執(zhí)行到相應(yīng)代碼的時候刃唐。

ES6 模塊和 CommonJS 相同點(diǎn)

模塊不會重復(fù)執(zhí)行

重復(fù)引入某個相同的模塊時羞迷,模塊只會執(zhí)行一次界轩。

循環(huán)依賴

CommonJS 模塊循環(huán)依賴

CommonJS 模塊的重要特性是加載時執(zhí)行,即腳本代碼在 require 的時候衔瓮,就會全部執(zhí)行浊猾。一旦出現(xiàn)某個模塊被“循環(huán)加載”,就只輸出已經(jīng)執(zhí)行的部分热鞍,還未執(zhí)行的部分不會輸出葫慎。

Demo 1
//a.js
exports.done = false;
var b = require('./b.js');
console.log('在 a.js 之中,b.done =', b.done);
exports.done = true;
console.log('a.js 執(zhí)行完畢');

上面代碼之中薇宠,a.js 腳本先輸出一個 done 變量偷办,然后加載另一個腳本文件 b.js。注意澄港,此時 a.js 代碼就停在這里椒涯,等待 b.js 執(zhí)行完畢,再往下執(zhí)行回梧。

再看 b.js 的代碼废岂。

//b.js
exports.done = false;
var a = require('./a.js');
console.log('在 b.js 之中,a.done =', a.done);
exports.done = true;
console.log('b.js 執(zhí)行完畢');

上面代碼之中狱意,b.js 執(zhí)行到第二行湖苞,就會去加載 a.js,這時详囤,就發(fā)生了“循環(huán)加載”财骨。系統(tǒng)會在 a.js 模塊對應(yīng)對象的 exports 屬性取值〔亟悖可是因?yàn)?a.js 還沒有執(zhí)行完蚓再,從 exports 屬性只能取回已經(jīng)執(zhí)行的部分,而不是最后的值包各。

a.js 已經(jīng)執(zhí)行的部分摘仅,只有一行。

exports.done = false;

因此问畅,對于 b.js來說娃属,它從 a.js 只輸入一個變量 done六荒,值為 false

然后矾端,b.js 接著往下執(zhí)行掏击,等到全部執(zhí)行完畢,再把執(zhí)行權(quán)交還給 a.js秩铆。于是砚亭,a.js 接著往下執(zhí)行,直到執(zhí)行完畢殴玛。我們寫一個腳本 main.js捅膘,驗(yàn)證這個過程。

var a = require('./a.js');
var b = require('./b.js');
console.log('在 main.js 之中, a.done=, b.done=', a.done, b.done);

執(zhí)行 main.js滚粟,運(yùn)行結(jié)果如下寻仗。

$ node main.js

在 b.js 之中,a.done = false
b.js 執(zhí)行完畢
在 a.js 之中凡壤,b.done = true
a.js 執(zhí)行完畢
在 main.js 之中, a.done = true, b.done = true

上面的代碼證明了2點(diǎn)

  • b.js 之中署尤,a.js 沒有執(zhí)行完畢,只執(zhí)行了第一行亚侠。
  • main.js 執(zhí)行到第二行時曹体,不會再次執(zhí)行 b.js,而是輸出緩存的 b.js 的執(zhí)行結(jié)果硝烂,即它的第四行箕别。
exports.done = true;

總之,CommonJS 輸入的是被輸出值的拷貝钢坦,不是引用究孕。

另外,由于 CommonJS 模塊遇到循環(huán)加載時爹凹,返回的是當(dāng)前已經(jīng)執(zhí)行的部分的值厨诸,而不是代碼全部執(zhí)行后的值,兩者可能會有差異禾酱。所以微酬,輸入變量的時候,必須非常小心颤陶。

var a = require('a'); // 安全的寫法 導(dǎo)入整體颗管,保證 module 已經(jīng)執(zhí)行完成
var foo = require('a').foo; // 危險(xiǎn)的寫法

exports.good = function (arg) {
  return a.foo('good', arg); // 使用的是 a.foo 的最新值
};

exports.bad = function (arg) {
  return foo('bad', arg); // 使用的是一個部分加載時的值
}; 

上面代碼中,如果發(fā)生循環(huán)加載滓走,require('a').foo 的值很可能后面會被改寫垦江,改用 require('a') 會更保險(xiǎn)一點(diǎn)。

Demo 2
// a.js
console.log('a starting');
exports.done = false;
const b = require('./b');
console.log('in a, b.done =', b.done);
exports.done = true;
console.log('a done');

// b.js
console.log('b starting');
exports.done = false;
const a = require('./a');
console.log('in b, a.done =', a.done);
exports.done = true;
console.log('b done');

// node a.js
// 執(zhí)行結(jié)果:
// a starting
// b starting
// in b, a.done = false
// b done
// in a, b.done = true
// a done

從上面的執(zhí)行過程中搅方,可以看到比吭,在 CommonJS 規(guī)范中绽族,當(dāng)遇到 require() 語句時,會執(zhí)行 require 模塊中的代碼衩藤,并緩存執(zhí)行的結(jié)果吧慢,當(dāng)下次再次加載時不會重復(fù)執(zhí)行,而是直接取緩存的結(jié)果赏表。正因?yàn)榇思焓霈F(xiàn)循環(huán)依賴時才不會出現(xiàn)無限循環(huán)調(diào)用的情況。

ES6 模塊循環(huán)依賴

跟 CommonJS 模塊一樣瓢剿,ES6 不會再去執(zhí)行重復(fù)加載的模塊逢慌,又由于 ES6 動態(tài)輸出綁定的特性,能保證 ES6 在任何時候都能獲取其它模塊當(dāng)前的最新值跋选。

ES6 動態(tài) import()

ES6 模塊在編譯時就會靜態(tài)分析涕癣,優(yōu)先于模塊內(nèi)的其他內(nèi)容執(zhí)行哗蜈,所以導(dǎo)致了我們無法寫出像下面這樣的代碼

if(some condition) {
  import a from './a';
}else {
  import b from './b';
}

// or 

import a from (str + 'b');

因?yàn)榫幾g時靜態(tài)分析前标,導(dǎo)致了我們在引用的時候,無法使用 條件語句 或者 拼接字符串模塊距潘,因?yàn)檫@些都是需要在運(yùn)行時才能確定的結(jié)果在 ES6 模塊是不被允許的炼列,所以 動態(tài)引入import() 應(yīng)運(yùn)而生。

import() 允許你在運(yùn)行時動態(tài)地引入 ES6 模塊音比,想到這俭尖,你可能也想起了 require.ensure 這個語法,但是它們的用途卻截然不同的洞翩。

require.ensure 的出現(xiàn)是 webpack 的產(chǎn)物稽犁,它是因?yàn)闉g覽器需要一種異步的機(jī)制可以用來異步加載模塊,從而減少初始的加載文件的體積骚亿,所以如果在服務(wù)端的話已亥, require.ensure 就無用武之地了,因?yàn)榉?wù)端不存在異步加載模塊的情況来屠,模塊同步進(jìn)行加載就可以滿足使用場景了虑椎。 CommonJS 模塊可以在運(yùn)行時確認(rèn)模塊加載。

import() 則不同俱笛,它主要是為了解決 ES6 模塊無法在運(yùn)行時確定模塊的引用關(guān)系捆姜,所以需要引入 import()

先來看下它的用法

  • 動態(tài)的 import() 提供一個基于 PromiseAPI
  • 動態(tài)的 import() 可以在腳本的任何地方使用 import() 接受字符串文字迎膜,可以根據(jù)需要構(gòu)造說明符
// a.js
const str = './b';
const flag = true;
if(flag) {
  import('./b').then(({foo}) => {
    console.log(foo);
  })
}
import(str).then(({foo}) => {
  console.log(foo);
})

// b.js
export const foo = 'foo';

// babel-node a.js
// 執(zhí)行結(jié)果
// foo
// foo

當(dāng)然泥技,如果在瀏覽器端的 import() 的用途就會變得更廣泛,比如 按需異步加載模塊磕仅,那么就和 require.ensure 功能類似了珊豹。

因?yàn)槭腔?Promise 的镊讼,所以如果你想要同時加載多個模塊的話,可以是 Promise.all 進(jìn)行并行異步加載平夜。

Promise.all([
  import('./a.js'),
  import('./b.js'),
  import('./c.js'),
]).then(([a, {default: b}, {c}]) => {
    console.log('a.js is loaded dynamically');
    console.log('b.js is loaded dynamically');
    console.log('c.js is loaded dynamically');
});

還有 Promise.race 方法蝶棋,它檢查哪個 Promise 被首先 resolvedreject。我們可以使用 import() 來檢查哪個 CDN 速度更快:

const CDNs = [
  {
    name: 'jQuery.com',
    url: 'https://code.jquery.com/jquery-3.1.1.min.js'
  },
  {
    name: 'googleapis.com',
    url: 'https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js'
  }
];

console.log(`------`);
console.log(`jQuery is: ${window.jQuery}`);

Promise.race([
  import(CDNs[0].url).then(()=>console.log(CDNs[0].name, 'loaded')),
  import(CDNs[1].url).then(()=>console.log(CDNs[1].name, 'loaded'))
]).then(()=> {
  console.log(`jQuery version: ${window.jQuery.fn.jquery}`);
});

當(dāng)然忽妒,如果你覺得這樣寫還不夠優(yōu)雅玩裙,也可以結(jié)合 async/await 語法糖來使用。

async function main() {
  const myModule = await import('./myModule.js');
  const {export1, export2} = await import('./myModule.js');
  const [module1, module2, module3] =
    await Promise.all([
      import('./module1.js'),
      import('./module2.js'),
      import('./module3.js'),
    ]);
}

動態(tài) import() 為我們提供了以異步方式使用 ES 模塊的額外功能段直。

根據(jù)我們的需求動態(tài)或有條件地加載它們吃溅,這使我們能夠更快,更好地創(chuàng)建更多優(yōu)勢應(yīng)用程序鸯檬。

8 webpack中加載3種模塊 | 語法

Webpack 允許使用不同的模塊類型决侈,但是底層必須使用同一種實(shí)現(xiàn)。所有的模塊可以直接在盒外運(yùn)行喧务。

  • ES6 模塊
import MyModule from './MyModule.js';
  • CommonJS(require)
var MyModule = require('./MyModule.js');
  • AMD
define(['./MyModule.js'], function (MyModule) {});

9 參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庐冯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坎穿,更是在濱河造成了極大的恐慌展父,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玲昧,死亡現(xiàn)場離奇詭異坯钦,居然都是意外死亡尿孔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爬橡,“玉大人阵幸,你說我怎么就攤上這事削咆】谏郑” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵菩收,是天一觀的道長梨睁。 經(jīng)常有香客問我,道長娜饵,這世上最難降的妖魔是什么坡贺? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上遍坟,老公的妹妹穿的比我還像新娘拳亿。我一直安慰自己,他們只是感情好愿伴,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布肺魁。 她就那樣靜靜地躺著,像睡著了一般隔节。 火紅的嫁衣襯著肌膚如雪鹅经。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天怎诫,我揣著相機(jī)與錄音瘾晃,去河邊找鬼。 笑死幻妓,一個胖子當(dāng)著我的面吹牛蹦误,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肉津,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼强胰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阀圾?” 一聲冷哼從身側(cè)響起哪廓,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤狗唉,失蹤者是張志新(化名)和其女友劉穎初烘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體分俯,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肾筐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缸剪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吗铐。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杏节,靈堂內(nèi)的尸體忽然破棺而出唬渗,到底是詐尸還是另有隱情,我是刑警寧澤奋渔,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布镊逝,位于F島的核電站,受9級特大地震影響嫉鲸,放射性物質(zhì)發(fā)生泄漏撑蒜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望座菠。 院中可真熱鬧狸眼,春花似錦、人聲如沸浴滴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽升略。三九已至司志,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間降宅,已是汗流浹背骂远。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腰根,地道東北人激才。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像额嘿,于是被迫代替她去往敵國和親瘸恼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349