摘要:即盡早地執(zhí)行依賴(lài)模塊。阮一峰輸出值的引用模塊是動(dòng)態(tài)關(guān)聯(lián)模塊中的值,輸出的是值得引用。的加載實(shí)現(xiàn)阮一峰運(yùn)行時(shí)加載靜態(tài)編譯模塊是運(yùn)行時(shí)加載,模塊是編譯時(shí)輸出接口。
模塊化開(kāi)發(fā) 優(yōu)點(diǎn)
模塊化開(kāi)發(fā)中,通常一個(gè)文件就是一個(gè)模塊,有自己的作用域,只向外暴露特定的變量和函數(shù),并且可以按需加載。
依賴(lài)自動(dòng)加載,按需加載。
提高代碼復(fù)用率,方便進(jìn)行代碼的管理,使得代碼管理更加清晰、規(guī)范。
減少了命名沖突,消除全局變量。
目前流行的js模塊化規(guī)范有CommonJS、AMD、CMD以及ES6的模塊系統(tǒng)
常見(jiàn)模塊化規(guī)范CommonJs (Node.js)
AMD (RequireJS)
CMD (SeaJS)
CommonJS(Node.js)CommonJS是服務(wù)器模塊的規(guī)范,Node.js采用了這個(gè)規(guī)范。
根據(jù) CommonJS 規(guī)范,一個(gè)多帶帶的文件就是一個(gè)模塊,每一個(gè)模塊都是一個(gè)多帶帶的作用域,在一個(gè)文件定義的變量(還包括函數(shù)和類(lèi)),都是私有的,對(duì)其他文件是不可見(jiàn)的。
CommonJS規(guī)范加載模塊是同步的,也就是說(shuō),只有加載完成,才能執(zhí)行后面的操作。
CommonJS 中,加載模塊使用 require 方法。該方法讀取一個(gè)文件并執(zhí)行,最后返回文件內(nèi)部的 exports 對(duì)象。
Node.js 主要用于服務(wù)器編程,加載的模塊文件一般都已經(jīng)存在本地硬盤(pán),加載起來(lái)較快,不用考慮異步加載的方式,所以 CommonJS 的同步加載模塊規(guī)范是比較適用的。但如果是瀏覽器環(huán)境,要從服務(wù)器加載模塊,這是就必須采用異步模式。所以就有了 AMD,CMD 等解決方案。
var x = 5; var addX = function(value) { return value + x; }; module.exports.x = x; module.exports.addX = addX; // 也可以改寫(xiě)為如下 module.exports = { x: x, addX: addX, };
let math = require("./math.js"); console.log("math.x",math.x); console.log("math.addX", math.addX(4));AMD (RequireJS) 異步模塊定義
AMD = Asynchronous Module Definition,即 異步模塊定義。
AMD 規(guī)范加載模塊是異步的,并允許函數(shù)回調(diào),不必等到所有模塊都加載完成,后續(xù)操作可以正常執(zhí)行。
AMD 中,使用 require 獲取依賴(lài)模塊,使用 exports 導(dǎo)出 API。
//規(guī)范 API define(id?, dependencies?, factory); define.amd = {}; // 定義無(wú)依賴(lài)的模塊 define({ add: function(x,y){ return x + y; } }); // 定義有依賴(lài)的模塊 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 是一個(gè)前端模塊化管理的工具庫(kù),遵循 AMD 規(guī)范,RequireJS 是對(duì) AMD 規(guī)范的闡述。
RequireJS 基本思想為,通過(guò)一個(gè)函數(shù)來(lái)將所有所需的或者所依賴(lài)的模塊裝載進(jìn)來(lái),然后返回一個(gè)新的函數(shù)(模塊)。后續(xù)所有的關(guān)于新模塊的業(yè)務(wù)代碼都在這個(gè)函數(shù)內(nèi)部操作。
RequireJS 要求每個(gè)模塊均放在獨(dú)立的文件之中,并使用 define 定義模塊,使用 require 方法調(diào)用模塊。
按照是否有依賴(lài)其他模塊情況,可以分為 獨(dú)立模塊 和 非獨(dú)立模塊。
獨(dú)立模塊,不依賴(lài)其他模塊,直接定義
define({ method1: function(){}, method2: function(){} }); //等價(jià)于 define(function() { return { method1: function(){}, method2: function(){} } });
非獨(dú)立模塊,依賴(lài)其他模塊
define([ "module1", "module2" ], function(m1, m2) { ... }); //等價(jià)于 define(function(require) { var m1 = require("module1"); var m2 = require("module2"); ... });
require 方法調(diào)用模塊
require(["foo", "bar"], function(foo, bar) { foo.func(); bar.func(); });CMD (SeaJS)
CMD = Common Module Definition,即 通用模塊定義。CMD 是 SeaJS 在推廣過(guò)程中對(duì)模塊定義的規(guī)范化產(chǎn)出。
CMD規(guī)范和AMD類(lèi)似,都主要運(yùn)行于瀏覽器端,寫(xiě)法上看起來(lái)也很類(lèi)似。主要是區(qū)別在于 模塊初始化時(shí)機(jī)
AMD中只要模塊作為依賴(lài)時(shí),就會(huì)加載并初始化
CMD中,模塊作為依賴(lài)且被引用時(shí)才會(huì)初始化,否則只會(huì)加載。
CMD 推崇依賴(lài)就近,AMD 推崇依賴(lài)前置。
AMD 的 API 默認(rèn)是一個(gè)當(dāng)多個(gè)用,CMD 嚴(yán)格的區(qū)分推崇職責(zé)單一。例如,AMD 里 require 分全局的和局部的。CMD里面沒(méi)有全局的 require,提供 seajs.use() 來(lái)實(shí)現(xiàn)模塊系統(tǒng)的加載啟動(dòng)。CMD 里每個(gè) API 都簡(jiǎn)單純粹。
//AMD define(["./a","./b"], function (a, b) { //依賴(lài)一開(kāi)始就寫(xiě)好 a.test(); b.test(); }); //CMD define(function (requie, exports, module) { //依賴(lài)可以就近書(shū)寫(xiě) var a = require("./a"); a.test(); ... //軟依賴(lài) if (status) { var b = requie("./b"); b.test(); } });Sea.js
Sea.js Github Page
SeaJS與RequireJS最大的區(qū)別
使用Sea.js,在書(shū)寫(xiě)文件時(shí),需要遵守CMD(Common Module Definition)模塊定義規(guī)范。一個(gè)文件就是一個(gè)模塊。
用法通過(guò) exports 暴露接口。這意味著不需要命名空間了,更不需要全局變量。這是一種徹底的命名沖突解決方案。
通過(guò) require 引入依賴(lài)。這可以讓依賴(lài)內(nèi)置,開(kāi)發(fā)者只需關(guān)心當(dāng)前模塊的依賴(lài),其他事情 Sea.js 都會(huì)自動(dòng)處理好。對(duì)模塊開(kāi)發(fā)者來(lái)說(shuō),這是一種很好的 關(guān)注度分離,能讓程序員更多地享受編碼的樂(lè)趣。
通過(guò) define 定義模塊,更多詳情參考SeasJS | 極客學(xué)院。
示例例如,對(duì)于下述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重寫(xiě)為
define(function(require, exports) { exports.each = function (arr) { // 實(shí)現(xiàn)代碼 }; exports.log = function (str) { // 實(shí)現(xiàn)代碼 }; });
通過(guò) exports 就可以向外提供接口。通過(guò) require("./util.js") 就可以拿到 util.js 中通過(guò) exports 暴露的接口。這里的 require 可以認(rèn)為是 Sea.js 給 JavaScript 語(yǔ)言增加的一個(gè)語(yǔ)法關(guān)鍵字,通過(guò) require 可以獲取其他模塊提供的接口。
define(function(require, exports) { var util = require("./util.js"); exports.init = function() { // 實(shí)現(xiàn)代碼 }; });SeaJS與RequireJS區(qū)別
二者區(qū)別主要表現(xiàn)在模塊初始化時(shí)機(jī)
AMD(RequireJS)中只要模塊作為依賴(lài)時(shí),就會(huì)加載并初始化。即盡早地執(zhí)行(依賴(lài))模塊。相當(dāng)于所有的require都被提前了,而且模塊執(zhí)行的順序也不一定100%就是require書(shū)寫(xiě)順序。
CMD(SeaJS)中,模塊作為依賴(lài)且被引用時(shí)才會(huì)初始化,否則只會(huì)加載。即只會(huì)在模塊真正需要使用的時(shí)候才初始化。模塊加載的順序是嚴(yán)格按照require書(shū)寫(xiě)的順序。
從規(guī)范上來(lái)說(shuō),AMD 更加簡(jiǎn)單且嚴(yán)謹(jǐn),適用性更廣,而在RequireJS強(qiáng)力的推動(dòng)下,在國(guó)外幾乎成了事實(shí)上的異步模塊標(biāo)準(zhǔn),各大類(lèi)庫(kù)也相繼支持AMD規(guī)范。
但從SeaJS與CMD來(lái)說(shuō),也做了很多不錯(cuò)東西:1、相對(duì)自然的依賴(lài)聲明風(fēng)格 2、小而美的內(nèi)部實(shí)現(xiàn) 3、貼心的外圍功能設(shè)計(jì) 4、更好的中文社區(qū)支持。
UMDUMD = Universal Module Definition,即通用模塊定義。UMD 是AMD 和 CommonJS的糅合。
AMD 模塊以瀏覽器第一的原則發(fā)展,異步加載模塊。
CommonJS 模塊以服務(wù)器第一原則發(fā)展,選擇同步加載。它的模塊無(wú)需包裝(unwrapped modules)。
這迫使人們又想出另一個(gè)更通用的模式 UMD(Universal Module Definition),實(shí)現(xiàn)跨平臺(tái)的解決方案。
UMD 先判斷是否支持 Node.js 的模塊(exports)是否存在,存在則使用 Node.js 模塊模式。再判斷是否支持 AMD(define 是否存在),存在則使用 AMD 方式加載模塊。
(function (window, factory) { if (typeof exports === "object") { module.exports = factory(); } else if (typeof define === "function" && define.amd) { define(factory); } else { window.eventUtil = factory(); } })(this, function () { //module ... });ES6 模塊 ES6模塊和CommonJS區(qū)別
ES6 模塊輸出的是值的引用,輸出接口動(dòng)態(tài)綁定,而 CommonJS 輸出的是值的拷貝。
CommonJS 模塊是運(yùn)行時(shí)加載,ES6 模塊是編譯時(shí)輸出接口。
CommonJS 輸出值的拷貝CommonJS 模塊輸出的是值的拷貝(類(lèi)比于基本類(lèi)型和引用類(lèi)型的賦值操作)。對(duì)于基本類(lèi)型,一旦輸出,模塊內(nèi)部的變化影響不到這個(gè)值。對(duì)于引用類(lèi)型,效果同引用類(lèi)型的賦值操作。
// 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" // Or console.log(require("./lib").counter); // 3 console.log(require("./lib").obj.name); // "Peter"
counter 是基本類(lèi)型值,模塊內(nèi)部值的變化不影響輸出的值變化。
obj 是引用類(lèi)型值,模塊內(nèi)部值的變化影響輸出的值變化。
上述兩點(diǎn)區(qū)別,類(lèi)比于基本類(lèi)型和引用類(lèi)型的賦值操作。
也可以借助取值函數(shù)(getter),將 counter 轉(zhuǎn)為引用類(lèi)型值,效果如下。
在類(lèi)的內(nèi)部,可以使用 get 和 set 關(guān)鍵字,對(duì)某個(gè)屬性設(shè)置存執(zhí)函數(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); // 4ES6 輸出值的引用
ES6 模塊是動(dòng)態(tài)關(guān)聯(lián)模塊中的值,輸出的是值得引用。原始值變了,import 加載的值也會(huì)跟著變。
ES6 模塊的運(yùn)行機(jī)制與 CommonJS 不一樣。JS 引擎對(duì)腳本靜態(tài)分析時(shí),遇到模塊加載命令 import,就會(huì)生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。ES6 模塊中,原始值變了,import 加載的值也會(huì)跟著變。因此,ES6 模塊是動(dòng)態(tài)引用,并且不會(huì)緩存值。 —— 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); // 4CommonJS 運(yùn)行時(shí)加載 ES6靜態(tài)編譯
CommonJS 模塊是運(yùn)行時(shí)加載,ES6 模塊是編譯時(shí)輸出接口。
這是因?yàn)椋?strong>CommonJS 加載的是一個(gè)對(duì)象(即 module.exports 屬性),該對(duì)象只有在腳本運(yùn)行完才會(huì)生成。而 ES6 模塊不是對(duì)象,它的對(duì)外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會(huì)生成。
ES6 模塊是編譯時(shí)輸出接口,因此有如下2個(gè)特點(diǎn)
import 命令會(huì)被 JS 引擎靜態(tài)分析,優(yōu)先于模塊內(nèi)的其他內(nèi)容執(zhí)行
export 命令會(huì)有變量聲明提升的效果
在文件中的任何位置引入 import 模塊都會(huì)被提前到文件頂部
// 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 引擎通過(guò)靜態(tài)分析,提到模塊執(zhí)行的最前面,優(yōu)于模塊中的其他部分的執(zhí)行。
由于 import 和 export 是靜態(tài)執(zhí)行,所以 import 和 export 具有變量提升效果。即 import 和 export 命令在模塊中的位置并不影響程序的輸出。
// 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í)行的。但具體對(duì)變量賦值需要等到執(zhí)行到相應(yīng)代碼的時(shí)候。
ES6模塊和CommonJS相同點(diǎn) 模塊不會(huì)重復(fù)執(zhí)行重復(fù)引入某個(gè)相同的模塊時(shí),模塊只會(huì)執(zhí)行一次。
循環(huán)依賴(lài) CommonJS 模塊循環(huán)依賴(lài)CommonJS 模塊的重要特性是加載時(shí)執(zhí)行,即腳本代碼在 require 的時(shí)候,就會(huì)全部執(zhí)行。一旦出現(xiàn)某個(gè)模塊被“循環(huán)加載”,就只輸出已經(jīng)執(zhí)行的部分,還未執(zhí)行的部分不會(huì)輸出。
//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 執(zhí)行完畢");
上面代碼之中,a.js 腳本先輸出一個(gè) done 變量,然后加載另一個(gè)腳本文件 b.js。注意,此時(shí) a.js 代碼就停在這里,等待 b.js 執(zhí)行完畢,再往下執(zhí)行。
再看 b.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 執(zhí)行完畢");
上面代碼之中,b.js 執(zhí)行到第二行,就會(huì)去加載 a.js,這時(shí),就發(fā)生了“循環(huán)加載”。系統(tǒng)會(huì) a.js 模塊對(duì)應(yīng)對(duì)象的 exports 屬性取值,可是因?yàn)?a.js 還沒(méi)有執(zhí)行完,從 exports 屬性只能取回已經(jīng)執(zhí)行的部分,而不是最后的值。
a.js 已經(jīng)執(zhí)行的部分,只有一行。
exports.done = false;
因此,對(duì)于 b.js來(lái)說(shuō),它從 a.js 只輸入一個(gè)變量 done,值為 false。
然后,b.js 接著往下執(zhí)行,等到全部執(zhí)行完畢,再把執(zhí)行權(quán)交還給 a.js。于是,a.js 接著往下執(zhí)行,直到執(zhí)行完畢。我們寫(xiě)一個(gè)腳本 main.js,驗(yàn)證這個(gè)過(guò)程。
var a = require("./a.js"); var b = require("./b.js"); console.log("在 main.js 之中, a.done=%j, b.done=%j", 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 沒(méi)有執(zhí)行完畢,只執(zhí)行了第一行
main.js 執(zhí)行到第二行時(shí),不會(huì)再次執(zhí)行 b.js,而是輸出緩存的 b.js 的執(zhí)行結(jié)果,即它的第四行。
exports.done = true;
總之,CommonJS 輸入的是被輸出值的拷貝,不是引用。
另外,由于 CommonJS 模塊遇到循環(huán)加載時(shí),返回的是當(dāng)前已經(jīng)執(zhí)行的部分的值,而不是代碼全部執(zhí)行后的值,兩者可能會(huì)有差異。所以,輸入變量的時(shí)候,必須非常小心。
var a = require("a"); // 安全的寫(xiě)法 導(dǎo)入整體,保證module已經(jīng)執(zhí)行完成 var foo = require("a").foo; // 危險(xiǎn)的寫(xiě)法 exports.good = function (arg) { return a.foo("good", arg); // 使用的是 a.foo 的最新值 }; exports.bad = function (arg) { return foo("bad", arg); // 使用的是一個(gè)部分加載時(shí)的值 };
上面代碼中,如果發(fā)生循環(huán)加載,require("a").foo 的值很可能后面會(huì)被改寫(xiě),改用 require("a") 會(huì)更保險(xiǎn)一點(diǎn)。
// 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í)行過(guò)程中,可以看到,在 CommonJS 規(guī)范中,當(dāng)遇到 require() 語(yǔ)句時(shí),會(huì)執(zhí)行 require 模塊中的代碼,并緩存執(zhí)行的結(jié)果,當(dāng)下次再次加載時(shí)不會(huì)重復(fù)執(zhí)行,而是直接取緩存的結(jié)果。正因?yàn)榇耍霈F(xiàn)循環(huán)依賴(lài)時(shí)才不會(huì)出現(xiàn)無(wú)限循環(huán)調(diào)用的情況。
ES6 模塊循環(huán)依賴(lài)跟 CommonJS 模塊一樣,ES6 不會(huì)再去執(zhí)行重復(fù)加載的模塊,又由于 ES6 動(dòng)態(tài)輸出綁定的特性,能保證 ES6 在任何時(shí)候都能獲取其它模塊當(dāng)前的最新值。
動(dòng)態(tài) import()ES6 模塊在編譯時(shí)就會(huì)靜態(tài)分析,優(yōu)先于模塊內(nèi)的其他內(nèi)容執(zhí)行,所以導(dǎo)致了我們無(wú)法寫(xiě)出像下面這樣的代碼
if(some condition) { import a from "./a"; }else { import b from "./b"; } // or import a from (str + "b");
因?yàn)榫幾g時(shí)靜態(tài)分析,導(dǎo)致了我們無(wú)法在條件語(yǔ)句或者拼接字符串模塊,因?yàn)檫@些都是需要在運(yùn)行時(shí)才能確定的結(jié)果在 ES6 模塊是不被允許的,所以 動(dòng)態(tài)引入import() 應(yīng)運(yùn)而生。
import() 允許你在運(yùn)行時(shí)動(dòng)態(tài)地引入 ES6 模塊,想到這,你可能也想起了 require.ensure 這個(gè)語(yǔ)法,但是它們的用途卻截然不同的。
require.ensure 的出現(xiàn)是 webpack 的產(chǎn)物,它是因?yàn)闉g覽器需要一種異步的機(jī)制可以用來(lái)異步加載模塊,從而減少初始的加載文件的體積,所以如果在服務(wù)端的話(huà), require.ensure 就無(wú)用武之地了,因?yàn)榉?wù)端不存在異步加載模塊的情況,模塊同步進(jìn)行加載就可以滿(mǎn)足使用場(chǎng)景了。 CommonJS 模塊可以在運(yùn)行時(shí)確認(rèn)模塊加載。
而 import() 則不同,它主要是為了解決 ES6 模塊無(wú)法在運(yùn)行時(shí)確定模塊的引用關(guān)系,所以需要引入 import()。
先來(lái)看下它的用法
動(dòng)態(tài)的 import() 提供一個(gè)基于 Promise 的 API
動(dòng)態(tài)的 import() 可以在腳本的任何地方使用 import() 接受字符串文字,可以根據(jù)需要構(gòu)造說(shuō)明符
// 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() 的用途就會(huì)變得更廣泛,比如 按需異步加載模塊,那么就和 require.ensure 功能類(lèi)似了。
因?yàn)槭腔?Promise 的,所以如果你想要同時(shí)加載多個(gè)模塊的話(huà),可以是 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 方法,它檢查哪個(gè) Promise 被首先 resolved 或 reject。我們可以使用 import() 來(lái)檢查哪個(gè) 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)然,如果你覺(jué)得這樣寫(xiě)還不夠優(yōu)雅,也可以結(jié)合 async/await 語(yǔ)法糖來(lái)使用。
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"), ]); }
動(dòng)態(tài) import() 為我們提供了以異步方式使用 ES 模塊的額外功能。
根據(jù)我們的需求動(dòng)態(tài)或有條件地加載它們,這使我們能夠更快,更好地創(chuàng)建更多優(yōu)勢(shì)應(yīng)用程序。
webpack中加載3種模塊 | 語(yǔ)法Webpack允許使用不同的模塊類(lèi)型,但是底層必須使用同一種實(shí)現(xiàn)。所有的模塊可以直接在盒外運(yùn)行。
ES6 模塊
import MyModule from "./MyModule.js";
CommonJS(Require)
var MyModule = require("./MyModule.js");
AMD
define(["./MyModule.js"], function (MyModule) { });參考資料
AMD, CMD, CommonJS和UMD | Segmentfault
JS模塊化加載之CommonJS、AMD、CMD、ES6
ES6 module的加載和實(shí)現(xiàn) | 阮一峰
前端模塊化開(kāi)發(fā)方案小對(duì)比
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109292.html
摘要:若不存在則模塊標(biāo)識(shí)應(yīng)該默認(rèn)定義為在加載器中被請(qǐng)求腳本的標(biāo)識(shí)。這也是目前很多插件頭部的寫(xiě)法,就是用來(lái)兼容各種不同模塊化的寫(xiě)法。語(yǔ)句輸出的值是動(dòng)態(tài)綁定的,綁定其所在的模塊。 前言 歷史上,js沒(méi)有模塊化的概念,不能把一個(gè)大工程分解成很多小模塊。這對(duì)于多人開(kāi)發(fā)大型,復(fù)雜的項(xiàng)目形成了巨大的障礙,明顯降低了開(kāi)發(fā)效率,java,Python有import,甚至連css都有@import,但是令人費(fèi)...
摘要:常見(jiàn)模塊化方案是由社區(qū)提出的模塊化方案中的一種,遵循了這套方案。是模塊化規(guī)范中的一種,遵循了這套規(guī)范。中的模塊化能力由兩個(gè)命令構(gòu)成和,命令用于規(guī)定模塊的對(duì)外接口,命令用于輸入其他模塊提供的功能。 為什么需要模塊化 在ES6出現(xiàn)之前,JS語(yǔ)言本身并沒(méi)有提供模塊化能力,這為開(kāi)發(fā)帶來(lái)了一些問(wèn)題,其中最重要的兩個(gè)問(wèn)題應(yīng)當(dāng)是全局污染和依賴(lài)管理混亂。 // file a.js var name =...
摘要:要想讓模塊再次運(yùn)行,必須清除緩存。模塊加載會(huì)阻塞接下來(lái)代碼的執(zhí)行,需要等到模塊加載完成才能繼續(xù)執(zhí)行同步加載。環(huán)境服務(wù)器環(huán)境應(yīng)用的模塊規(guī)范是參照實(shí)現(xiàn)的。這等同在每個(gè)模塊頭部,有一行這樣的命令。 commonJS 特點(diǎn): 1、模塊可以多次加載,但是只會(huì)在第一次加載時(shí)運(yùn)行一次,然后運(yùn)行結(jié)果就被緩存了,以后再加載,就直接讀取緩存結(jié)果。要想讓模塊再次運(yùn)行,必須清除緩存。2、模塊加載會(huì)阻塞接下來(lái)代...
摘要:參考資料前端模塊化詳解完整版入門(mén)近一萬(wàn)字的語(yǔ)法知識(shí)點(diǎn)補(bǔ)充徹底搞清楚中的和和詳解 前言 前端的模塊化之路經(jīng)歷了漫長(zhǎng)的過(guò)程,想詳細(xì)了解的小伙伴可以看浪里行舟大神寫(xiě)的前端模塊化詳解(完整版),這里根據(jù)幾位大佬們寫(xiě)的文章,將模塊化規(guī)范部分做了匯總和整理,希望讀完的小伙伴能有些收獲,也希望覺(jué)得有用的小伙伴可以點(diǎn)個(gè)贊,筆芯。 什么是模塊 將一個(gè)復(fù)雜的程序依據(jù)一定的規(guī)則(規(guī)范)封裝成幾個(gè)塊(文件)...
摘要:依賴(lài)全部加載完成后,調(diào)用回調(diào)函數(shù)規(guī)范異步加載模塊規(guī)范和很相似,簡(jiǎn)單,并與和的規(guī)范保持了很大的兼容性在規(guī)范中,一個(gè)模塊就是一個(gè)文件。 拋出問(wèn)題: 在開(kāi)發(fā)中在導(dǎo)入模塊時(shí)經(jīng)常使用require和import; 導(dǎo)出模塊時(shí)使用module.exports/exports或者export/export default; 有時(shí)候?yàn)榱艘靡粋€(gè)模塊會(huì)使用require奇怪的是也可以使用import?...
閱讀 2993·2021-11-23 09:51
閱讀 3009·2021-11-02 14:46
閱讀 874·2021-11-02 14:45
閱讀 2753·2021-09-23 11:57
閱讀 2504·2021-09-23 11:22
閱讀 1934·2019-08-29 16:29
閱讀 753·2019-08-29 16:16
閱讀 948·2019-08-26 13:44