欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析JavaScript中五種模塊系統(tǒng)的使用

 更新時間:2022年11月28日 08:40:23   作者:前端西瓜哥  
模塊系統(tǒng)是什么?簡單來說,其實就是我們在一個文件里寫代碼,聲明一些可以導(dǎo)出的字段,然后另一個文件可以將其導(dǎo)入并使用。今天我們來聊聊?JavaScript?的模塊系統(tǒng),感興趣的可以了解一下

模塊系統(tǒng)

模塊系統(tǒng)是什么?簡單來說,其實就是我們在一個文件里寫代碼,聲明一些可以導(dǎo)出的字段,然后另一個文件可以將其導(dǎo)入并使用。

模塊化的優(yōu)點:

  • 文件里聲明的變量會被隔離,不會暴露到全局,可以有效解決以往變量污染全局空間的問題;
  • 更容易看出代碼之間的依賴關(guān)系,看文件頭的的導(dǎo)入代碼就知道;
  • 方便多人協(xié)作,各自開發(fā)自己的模塊,而不沖突;
  • 不用擔(dān)心文件引入的順序;
  • 方便以文件為單位做單元測試;

模塊化解決了變量污染、代碼維護、依賴順序問題。

CommonJS

CommonJS,或者叫 CJS,是 nodejs 選擇的模塊化標(biāo)準(zhǔn)。

導(dǎo)出模塊的寫法:

// 集中一起導(dǎo)出
module.exports = {
  userName: '前端西瓜哥',
  // ...
}

// 或分散導(dǎo)出
module.exports.userName = '前端西瓜哥';

// 或
exports.userName = '前端西瓜哥';

每個文件都可以訪問到一個 module 對象,其下的 exports 屬性是一個空對象,你可以給它加上屬性,module.exports 將作為可以導(dǎo)出的代碼部分。

exports 是一個別名,它指向 module.exports,用它能夠少打一點字。

然后是導(dǎo)入模塊的寫法:

const Setting = require('./user');

// 或用解構(gòu)寫法,直接提取屬性
const { userName } = require('./user');

// 或不使用任何導(dǎo)出內(nèi)容,但希望指定對應(yīng)模塊文件的副作用(如給全局注入變量)
require('./user');

require 方法能夠找到對應(yīng)模塊文件,提取出它的 module.exports 對象,引入到當(dāng)前模塊中。關(guān)于 require 怎么找到模塊的過程,也是一篇長篇大論,有機會另開一篇文章再講解了。

nodejs 現(xiàn)在最新版也支持用 ES Modules 的方式了,需要在 package.json 上加上  "type": "module"

ES Modules

ES Modules,或者叫 ESM,是 ES6 引入的新特性,是模塊系統(tǒng)的真正標(biāo)準(zhǔn)。

在 script 標(biāo)簽下設(shè)置 type="module",可以開啟模塊化加載。

當(dāng)然實際上生產(chǎn)環(huán)境我們是不會這么做的,只是用 ES Modules 寫代碼,然后打包,用傳統(tǒng)的模式運行。

導(dǎo)出模塊的寫法:

// 集中一起導(dǎo)出
export {
  userName: '前端西瓜哥',
  // ...
}

// 或分散指定
export const userName = '前端西瓜哥';

// 一種特殊的導(dǎo)出:默認(rèn)指定
export default user;

ES Modules 中,export 不是一個對象,準(zhǔn)確來說都不是變量,而是新引入的關(guān)鍵字,用于指定要導(dǎo)出的變量。

然后是導(dǎo)入模塊的寫法:

// 導(dǎo)入需要的變量
import { userName } from './user.js';

// 導(dǎo)入 export default 對應(yīng)的變量,這時候不需要花括號
import user from './user.js';

// 在命名沖突時,可以給導(dǎo)入的變量換個名字
import user as otherUser from './user.js';

// 將 export 組裝成對象,包括 export default,對應(yīng) default 屬性名
import * as obj from './user.js'

根據(jù)標(biāo)準(zhǔn),導(dǎo)入的模塊名是要提供 .js 后綴名的,因為實際上的 ES Module 是會請求一個 url 的,url 必須精準(zhǔn)。

不過我們通常會使用打包工具,可以配置一下將其省略。比如 webpack,我們可以設(shè)置 resolve.extensions 配置項來設(shè)置后綴不存在時,拼上什么后綴去查找。

AMD

AMD 標(biāo)準(zhǔn)已過時,不必花太多精力學(xué)習(xí),簡單了解即可。

AMD,是 Asynchronous Module Definition 的縮寫。這是一種異步的模塊加載方案,是 ES Module 發(fā)布前的一種瀏覽器模塊化方案。

CommonJS 不適合瀏覽器端,因為它的模塊加載是同步的,瀏覽器需要請求模塊文件,是異步的。

AMD 的特點是 依賴前置,即所有的依賴模塊要在開頭指定好。

實現(xiàn) AMD 規(guī)范的典型庫是 require.js,require.js 的使用如下。

導(dǎo)出模塊寫法為:

define(["./cart", "./inventory"], function (cart, inventory) {
  // 從 cart.js 和 inventory.js 拿到對應(yīng)的導(dǎo)出內(nèi)容
  // 然后通過返回值指定當(dāng)前模塊的導(dǎo)出內(nèi)容
  return {
    color: "blue",
    size: "large",
    addToCart: function () {
      inventory.decrement(this);
      cart.add(this);
    },
  };
});

需要用 define 函數(shù),第一參數(shù)為依賴的其他模塊,然后第二個參數(shù)函數(shù)可以拿到這些模塊導(dǎo)出的內(nèi)容,然后這個函數(shù)的返回值就是當(dāng)前文件的導(dǎo)出內(nèi)容。

require 方法為主模塊,也就是程序入口。

require(["a", "b"], function(util) {
  // a.js 和 b.js 加載完會執(zhí)行這個函數(shù)
})

CMD

CMD 也過時了,甚至比 AMD 還小眾,同樣簡單了解即可。

CMD,全稱 Common Module Definition,也是瀏覽器模塊化的一種方案,和 AMD 是同時代的產(chǎn)物。

但和 AMD 不同的是,它的特點是 依賴就近,在具體的用到某個模塊的地方引入即可,接近 CommonJS。

代表庫是 sea.js,我們看看語法。

模塊寫法:

define(function(require, exports, module) {
  // 導(dǎo)入 a 模塊
 var a = require('./a');
  a.doSomething();
  
  // 導(dǎo)出當(dāng)前模塊
 module.exports = {
    name: 'b',
    start: function() {};
  };
});

主模塊這樣寫:

seajs.use(['./a', './b'], function(a, b) {
  a.doSomething();
  b.start();
});

寫法很像 Commonjs 了。

ES Modules 和 CommonJS 的區(qū)別

  • Commonjs 模塊在 運行時 加載,ESM 在 編譯時 確定依賴關(guān)系;
  • require 可以在代碼的任何地方使用,比如在條件語句內(nèi),因為它是運行時同步加載的。import 需要寫在模塊文件最外層,不能在其他任何作用域內(nèi),且 import 會做提升;
  • require 永遠(yuǎn)是同步加載代碼。import 一般也是同步的,但也能做動態(tài)加載,此時則是異步的。如 import('lang/zh.js').then(module) => { /* 獲得語言包對象 */ }。
  • CommonJS 的導(dǎo)出是值拷貝,而 ESM的導(dǎo)出是值引用。比如 a.js 導(dǎo)出一個值為 1 的 count,b.js 導(dǎo)入 a.js 拿到 count,然后 a.js 中的定時器修改了 count 的值為 2,然后在 b.js 再打印 count 的值。你會發(fā)現(xiàn) Commonjs 下,count 還是 1,而在 ESM 下則變成了 2(說明編譯時做了一些處理)。
  • ESM import 的變量是只讀的,等價于用 const 聲明;CommonJS 則隨意用聲明關(guān)鍵字,var、let、const 都可以。
  • CommonJS 可以導(dǎo)入 json 文件,ESM 不可以(實際上我們使用打包工具,通過轉(zhuǎn)換器支持各種文件的導(dǎo)入);

UMD

模塊標(biāo)準(zhǔn)這么多,需要一個個構(gòu)建不同的模塊文件可太麻煩了。如果我只希望發(fā)布一份代碼,就讓它運行在不同的模塊系統(tǒng)中,有辦法嗎?

可以考慮用 UMD(Universal Module Definition),它能夠同時在 CommonJS、AMD 運行,如果都不是,則會暴露到全局環(huán)境中。

下面是 webpack(5.74.0)設(shè)置 output.libraryTarget"umd" 后的打包結(jié)果的部分代碼。

(function (root, factory) {
  // 判斷是否為 commonjs
 if(typeof exports === 'object' && typeof module === 'object')
  module.exports = factory();
  // 判斷是否為 requirejs
 else if(typeof define === 'function' && define.amd)
  define([], factory);
 else {
  var a = factory();
  for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 }
})(self, () => {
  // 模塊內(nèi)容
  return {
    user: '前端西瓜哥'
  }
})

原理也不復(fù)雜,就是先通過判斷不同模塊系統(tǒng)提供的全局變量是否存在,且類型符合預(yù)期,就能知道是 CommonJS 還是 requirejs,然后使用它們對應(yīng)的語法。都不匹配,那就暴露到全局。

雖然但是,UMD 無法支持 ES Modules,因為它的 import 不是變量,而是一個關(guān)鍵字,是編程語言層面的語法,在其他模塊系統(tǒng)中存在會報錯,且 import 只能在模塊最外邊使用。

結(jié)尾

模塊系統(tǒng)太多,讓我們有不小的心智負(fù)擔(dān),但正統(tǒng)在 ES Modules,nodejs 現(xiàn)在也完全支持 ES Modules 了。但因為歷史問題,我們還是要保留 CommonJS。

到此這篇關(guān)于淺析JavaScript中五種模塊系統(tǒng)的使用的文章就介紹到這了,更多相關(guān)JavaScript模塊系統(tǒng)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論