JavaScript模塊化方案和工具都有哪些

這篇文章給大家介紹JavaScript模塊化方案和工具都有哪些,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

田東ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!

模塊化是大型前端項(xiàng)目的必備要素。JavaScript 從誕生至今,出現(xiàn)過各種各樣的模塊化方案,讓我們一起來盤點(diǎn)下吧。

IIFE 模塊

默認(rèn)情況下,在瀏覽器宿主環(huán)境里定義的變量都是全局變量,如果頁面引用了多個(gè)這樣的 JavaScript 文件,很容易造成命名沖突。

// 定義全局變量 let count = 0; const increase = () => ++count; const reset = () => {     count = 0;     console.log("Count is reset."); };  // 使用全局變量 increase(); reset();

為了避免全局污染,可以用匿名函數(shù)包裹起來,這就是最簡(jiǎn)單的 IIFE 模塊(立即執(zhí)行的函數(shù)表達(dá)式):

// 定義 IIFE 模塊 const iifeCounterModule = (() => {     let count = 0;     return {         increase: () => ++count,         reset: () => {             count = 0;             console.log("Count is reset.");         }     }; })();  // 使用 IIFE 模塊 iifeCounterModule.increase(); iifeCounterModule.reset();

IIFE 只暴露了一個(gè)全局的模塊名,內(nèi)部都是局部變量,大大減少了全局命名沖突。

每個(gè) IIFE 模塊都是一個(gè)全局變量,這些模塊通常有自己的依賴。可以在模塊內(nèi)部直接使用依賴的全局變量,也可以把依賴作為參數(shù)傳給 IIFE:

// 定義帶有依賴的 IIFE 模塊 const iifeCounterModule = ((dependencyModule1, dependencyModule2) => {     let count = 0;     return {         increase: () => ++count,         reset: () => {             count = 0;             console.log("Count is reset.");         }     }; })(dependencyModule1, dependencyModule2);

一些流行的庫在早期版本都采用這模式,比如大名鼎鼎的 jQuery(最新版本也開始用 UMD 模塊了,后面會(huì)介紹)。

還有一種 IIFE,在 API 聲明上遵循了一種格式,就是在模塊內(nèi)部提前定義了這些 API 對(duì)應(yīng)的變量,方便 API 之間互相調(diào)用:

// Define revealing module. const revealingCounterModule = (() => {     let count = 0;     const increase = () => ++count;     const reset = () => {         count = 0;         console.log("Count is reset.");     };      return {         increase,         reset     }; })();  // Use revealing module. revealingCounterModule.increase(); revealingCounterModule.reset();

CommonJS 模塊(Node.js 模塊)

CommonJS 最初叫 ServerJS,是由 Node.js 實(shí)現(xiàn)的模塊化方案。默認(rèn)情況下,每個(gè) .js  文件就是一個(gè)模塊,模塊內(nèi)部提供了一個(gè)module和exports變量,用于暴露模塊的 API。使用 require  加載和使用模塊。下面這段代碼定義了一個(gè)計(jì)數(shù)器模塊:

// 定義 CommonJS 模塊: commonJSCounterModule.js. const dependencyModule1 = require("./dependencyModule1"); const dependencyModule2 = require("./dependencyModule2");  let count = 0; const increase = () => ++count; const reset = () => {     count = 0;     console.log("Count is reset."); };  exports.increase = increase; exports.reset = reset; // 或者這樣: module.exports = {     increase,     reset };

使用這個(gè)模塊:

// 使用 CommonJS 模塊 const { increase, reset } = require("./commonJSCounterModule"); increase(); reset(); // 或者這樣: const commonJSCounterModule = require("./commonJSCounterModule"); commonJSCounterModule.increase(); commonJSCounterModule.reset();

在運(yùn)行時(shí),Node.js 會(huì)將文件內(nèi)的代碼包裹在一個(gè)函數(shù)內(nèi),然后通過參數(shù)傳遞exports、module變量和require函數(shù)。

// Define CommonJS module: wrapped commonJSCounterModule.js. (function (exports, require, module, __filename, __dirname) {     const dependencyModule1 = require("./dependencyModule1");     const dependencyModule2 = require("./dependencyModule2");      let count = 0;     const increase = () => ++count;     const reset = () => {         count = 0;         console.log("Count is reset.");     };      module.exports = {         increase,         reset     };      return module.exports; }).call(thisValue, exports, require, module, filename, dirname);  // Use CommonJS module. (function (exports, require, module, __filename, __dirname) {     const commonJSCounterModule = require("./commonJSCounterModule");     commonJSCounterModule.increase();     commonJSCounterModule.reset(); }).call(thisValue, exports, require, module, filename, dirname);

AMD 模塊(RequireJS 模塊)

AMD(異步模塊定義)也是一種模塊格式,由 RequireJS  這個(gè)庫實(shí)現(xiàn)。它通過define函數(shù)定義模塊,并接受模塊名和依賴的模塊名作為參數(shù)。

// 定義 AMD 模塊 define("amdCounterModule", ["dependencyModule1", "dependencyModule2"],        (dependencyModule1, dependencyModule2) => {     let count = 0;     const increase = () => ++count;     const reset = () => {         count = 0;         console.log("Count is reset.");     };      return {         increase,         reset     }; });

也用 require加載和使用模塊:

require(["amdCounterModule"], amdCounterModule => {     amdCounterModule.increase();     amdCounterModule.reset(); });

跟 CommonJS 不同,這里的 requrie接受一個(gè)回調(diào)函數(shù),參數(shù)就是加載好的模塊對(duì)象。

AMD 的define函數(shù)還可以動(dòng)態(tài)加載模塊,只要給它傳一個(gè)回調(diào)函數(shù),并帶上 require參數(shù):

// Use dynamic AMD module. define(require => {     const dynamicDependencyModule1 = require("dependencyModule1");     const dynamicDependencyModule2 = require("dependencyModule2");      let count = 0;     const increase = () => ++count;     const reset = () => {         count = 0;         console.log("Count is reset.");     };      return {         increase,         reset     }; });

AMD 模塊還可以給define傳遞module和exports,這樣就可以在內(nèi)部使用 CommonJS 代碼:

// 定義帶有 CommonJS 代碼的 AMD 模塊 define((require, exports, module) => {     // CommonJS 代碼     const dependencyModule1 = require("dependencyModule1");     const dependencyModule2 = require("dependencyModule2");      let count = 0;     const increase = () => ++count;     const reset = () => {         count = 0;         console.log("Count is reset.");     };      exports.increase = increase;     exports.reset = reset; });  // 使用帶有 CommonJS 代碼的 AMD 模塊 define(require => {     // CommonJS 代碼     const counterModule = require("amdCounterModule");     counterModule.increase();     counterModule.reset(); });

UMD 模塊

UMD(通用模塊定義),是一種支持多種環(huán)境的模塊化格式,可同時(shí)用于 AMD 和 瀏覽器(或者 Node.js)環(huán)境。

兼容 AMD 和瀏覽器全局引入:

((root, factory) => {     // 檢測(cè)是否存在 AMD/RequireJS 的 define 函數(shù)     if (typeof define === "function" && define.amd) {         // 如果是,在 define 函數(shù)內(nèi)調(diào)用 factory         define("umdCounterModule", ["deependencyModule1", "dependencyModule2"], factory);     } else {         // 否則為瀏覽器環(huán)境,直接調(diào)用 factory         // 導(dǎo)入的依賴是全局變量(window 對(duì)象的屬性)         // 導(dǎo)出的模塊也是全局變量(window 對(duì)象的屬性)         root.umdCounterModule = factory(root.deependencyModule1, root.dependencyModule2);     } })(typeof self !== "undefined" ? self : this, (deependencyModule1, dependencyModule2) => {     // 具體的模塊代碼     let count = 0;     const increase = () => ++count;     const reset = () => {         count = 0;         console.log("Count is reset.");     };      return {         increase,         reset     }; });

看起來很復(fù)雜,其實(shí)就是個(gè) IIFE。代碼注釋寫得很清楚了,可以看看。

下面來看兼容 AMD 和 CommonJS(Node.js)模塊的 UMD:

(define => define((require, exports, module) => {     // 模塊代碼     const dependencyModule1 = require("dependencyModule1");     const dependencyModule2 = require("dependencyModule2");      let count = 0;     const increase = () => ++count;     const reset = () => {         count = 0;         console.log("Count is reset.");     };      module.export = {         increase,         reset     }; }))(// 判斷 CommonJS 里的 module 變量和 exports 變量是否存在     // 同時(shí)判斷 AMD/RequireJS 的define 函數(shù)是否存在     typeof module === "object" && module.exports && typeof define !== "function"         ? // 如果是 CommonJS/Node.js,手動(dòng)定義一個(gè) define 函數(shù)             factory => module.exports = factory(require, exports, module)         : // 否則是 AMD/RequireJS,直接使用 define 函數(shù)             define);

同樣是個(gè) IIFE,通過判斷環(huán)境,選擇執(zhí)行對(duì)應(yīng)的代碼。

ES 模塊(ES6 Module)

前面說到的幾種模塊格式,都是用到了各種技巧實(shí)現(xiàn)的,看起來眼花繚亂。終于,在 2015 年,ECMAScript 第 6  版(ES 2015,或者 ES6 )橫空出世!它引入了一種全新的模塊格式,主要語法就是 import和epxort關(guān)鍵字。來看 ES6 怎么定義模塊:

// 定義 ES 模塊:esCounterModule.js 或 esCounterModule.mjs. import dependencyModule1 from "./dependencyModule1.mjs"; import dependencyModule2 from "./dependencyModule2.mjs";  let count = 0; // 具名導(dǎo)出: export const increase = () => ++count; export const reset = () => {     count = 0;     console.log("Count is reset."); }; // 默認(rèn)導(dǎo)出 export default {     increase,     reset };

瀏覽器里使用該模塊,在 script標(biāo)簽上加上type="module",表明引入的是 ES 模塊。在 Node.js 環(huán)境中使用時(shí),把擴(kuò)展名改成  .mjs。

// Use ES module. //瀏覽器: <script type="module" src="esCounterModule.js"></script> or inline.  // 服務(wù)器:esCounterModule.mjs import { increase, reset } from "./esCounterModule.mjs"; increase(); reset(); // Or import from default export: import esCounterModule from "./esCounterModule.mjs"; esCounterModule.increase(); esCounterModule.reset();

瀏覽器如果不支持,可以加個(gè)兜底屬性:

<script nomodule>     alert("Not supported."); </script>

ES 動(dòng)態(tài)模塊(ECMAScript 2020)

2020 年最新的 ESCMA 標(biāo)準(zhǔn)11版中引入了內(nèi)置的 import函數(shù),用于動(dòng)態(tài)加載 ES  模塊。import函數(shù)返回一個(gè) Promise,在它的then回調(diào)里使用加載后的模塊:

// 用 Promise API 加載動(dòng)態(tài) ES 模塊 import("./esCounterModule.js").then(({ increase, reset }) => {     increase();     reset(); });  import("./esCounterModule.js").then(dynamicESCounterModule => {     dynamicESCounterModule.increase();     dynamicESCounterModule.reset(); });

由于返回的是 Promise,那肯定也支持await用法:

// 通過 async/await 使用 ES 動(dòng)態(tài)模塊 (async () => {     // 具名導(dǎo)出的模塊     const { increase, reset } = await import("./esCounterModule.js");     increase();     reset();     // 默認(rèn)導(dǎo)出的模塊     const dynamicESCounterModule = await import("./esCounterModule.js");     dynamicESCounterModule.increase();     dynamicESCounterModule.reset(); })();

各平臺(tái)對(duì)import、export和動(dòng)態(tài)import的兼容情況如下:

JavaScript模塊化方案和工具都有哪些

image.png

JavaScript模塊化方案和工具都有哪些

image.png

System 模塊

SystemJS 是一個(gè) ES 模塊語法轉(zhuǎn)換庫,以便支持低版本的 ES。例如,下面的模塊是用 ES6 語法定義的:

// 定義 ES 模塊 import dependencyModule1 from "./dependencyModule1.js"; import dependencyModule2 from "./dependencyModule2.js"; dependencyModule1.api1(); dependencyModule2.api2();  let count = 0; // Named export: export const increase = function () { return ++count }; export const reset = function () {     count = 0;     console.log("Count is reset."); }; // Or default export: export default {     increase,     reset }

如果當(dāng)前的運(yùn)行環(huán)境(比如舊瀏覽器)不支持 ES6 語法,上面的代碼就無法運(yùn)行。一種方案是把上面的模塊定義轉(zhuǎn)換成 SystemJS 庫的一個(gè) API,  System.register:

// Define SystemJS module. System.register(["./dependencyModule1.js", "./dependencyModule2.js"],                  function (exports_1, context_1) {     "use strict";     var dependencyModule1_js_1, dependencyModule2_js_1, count, increase, reset;     var __moduleName = context_1 && context_1.id;     return {         setters: [             function (dependencyModule1_js_1_1) {                 dependencyModule1_js_1 = dependencyModule1_js_1_1;             },             function (dependencyModule2_js_1_1) {                 dependencyModule2_js_1 = dependencyModule2_js_1_1;             }         ],         execute: function () {             dependencyModule1_js_1.default.api1();             dependencyModule2_js_1.default.api2();             count = 0;             // Named export:             exports_1("increase", increase = function () { return ++count };             exports_1("reset", reset = function () {                 count = 0;                 console.log("Count is reset.");             };);             // Or default export:             exports_1("default", {                 increase,                 reset             });         }     }; });

這樣,import/export關(guān)鍵字就不見了。Webpack、TypeScript 等可以自動(dòng)完成這樣的轉(zhuǎn)換(后面會(huì)講)。

SystemJS 也支持動(dòng)態(tài)加載模塊:

// Use SystemJS module with promise APIs. System.import("./esCounterModule.js").then(dynamicESCounterModule => {     dynamicESCounterModule.increase();     dynamicESCounterModule.reset(); });

Webpack 模塊(打包 AMD,CJS,ESM)

Webpack 是個(gè)強(qiáng)大的模塊打包工具,可以將 AMD、CommonJS 和 ES Module  格式的模塊轉(zhuǎn)換并打包到單個(gè) JS 文件。

Babel 模塊

Babel 是也個(gè)轉(zhuǎn)換器,可將 ES6+ 代碼轉(zhuǎn)換成低版本的 ES。前面例子中的計(jì)數(shù)器模塊用 Babel 轉(zhuǎn)換后的代碼是這樣的:

// Babel. Object.defineProperty(exports, "__esModule", {     value: true }); exports["default"] = void 0; function _interopRequireDefault(obj)           { return obj && obj.__esModule ? obj : { "default": obj }; }  // Define ES module: esCounterModule.js. var dependencyModule1 = _interopRequireDefault(require("./amdDependencyModule1")); var dependencyModule2 = _interopRequireDefault(require("./commonJSDependencyModule2")); dependencyModule1["default"].api1(); dependencyModule2["default"].api2();  var count = 0; var increase = function () { return ++count; }; var reset = function () {     count = 0;     console.log("Count is reset."); };  exports["default"] = {     increase: increase,     reset: reset };

引入該模塊的index.js將會(huì)轉(zhuǎn)換成:

// Babel. function _interopRequireDefault(obj)           { return obj && obj.__esModule ? obj : { "default": obj }; }  // Use ES module: index.js var esCounterModule = _interopRequireDefault(require("./esCounterModule.js")); esCounterModule["default"].increase(); esCounterModule["default"].reset();

以上是 Babel 的默認(rèn)轉(zhuǎn)換行為,它還可以結(jié)合其他插件使用,比如前面提到的 SystemJS。經(jīng)過配置,Babel 可將 AMD、CJS、ES  Module 轉(zhuǎn)換成 System 模塊格式。

TypeScript 模塊

TypeScript 是 JavaScript 的超集,可以支持所有 JavaScript 語法,包括 ES6  模塊語法。它在轉(zhuǎn)換時(shí),可以保留 ES6 語法,也可以轉(zhuǎn)換成 AMD、CJS、UMD、SystemJS 等格式,取決于配置:

{     "compilerOptions": {         "module": "ES2020", // None, CommonJS, AMD, System, UMD, ES6, ES2015, ES2020, ESNext.     } }

TypeScript 還支持 module和namespace關(guān)鍵字,表示內(nèi)部模塊。

module Counter {     let count = 0;     export const increase = () => ++count;     export const reset = () => {         count = 0;         console.log("Count is reset.");     }; }  namespace Counter {     let count = 0;     export const increase = () => ++count;     export const reset = () => {         count = 0;         console.log("Count is reset.");     }; }

都可以轉(zhuǎn)換成 JavaScript 對(duì)象:

var Counter; (function (Counter) {     var count = 0;     Counter.increase = function () { return ++count; };     Counter.reset = function () {         count = 0;         console.log("Count is reset.");     }; })(Counter || (Counter = {}));

隨著標(biāo)準(zhǔn)化推進(jìn),Node.js  和最新的現(xiàn)代瀏覽器都開始支持 ES 模塊格式。如果要在舊環(huán)境中使用模塊化,可以通過 Webpack、Babel、TypeScript、SystemJS  等工具進(jìn)行轉(zhuǎn)換。

關(guān)于JavaScript模塊化方案和工具都有哪些就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

當(dāng)前標(biāo)題:JavaScript模塊化方案和工具都有哪些
路徑分享:http://muchs.cn/article22/ishocc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、網(wǎng)站導(dǎo)航、做網(wǎng)站虛擬主機(jī)、移動(dòng)網(wǎng)站建設(shè)微信公眾號(hào)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)