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

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

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

模塊系統(tǒng)

模塊系統(tǒng)是什么?簡(jiǎn)單來(lái)說(shuō),其實(shí)就是我們?cè)谝粋€(gè)文件里寫代碼,聲明一些可以導(dǎo)出的字段,然后另一個(gè)文件可以將其導(dǎo)入并使用。

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

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

模塊化解決了變量污染、代碼維護(hù)、依賴順序問(wèn)題。

CommonJS

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

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

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

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

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

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

exports 是一個(gè)別名,它指向 module.exports,用它能夠少打一點(diǎn)字。

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

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

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

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

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

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)然實(shí)際上生產(chǎn)環(huán)境我們是不會(huì)這么做的,只是用 ES Modules 寫代碼,然后打包,用傳統(tǒng)的模式運(yùn)行。

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

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

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

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

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

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

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

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

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

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

根據(jù)標(biāo)準(zhǔn),導(dǎo)入的模塊名是要提供 .js 后綴名的,因?yàn)閷?shí)際上的 ES Module 是會(huì)請(qǐng)求一個(gè) url 的,url 必須精準(zhǔn)。

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

AMD

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

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

CommonJS 不適合瀏覽器端,因?yàn)樗哪K加載是同步的,瀏覽器需要請(qǐng)求模塊文件,是異步的。

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

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

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

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

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

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

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

CMD

CMD 也過(guò)時(shí)了,甚至比 AMD 還小眾,同樣簡(jiǎn)單了解即可。

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

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

代表庫(kù)是 sea.js,我們看看語(yǔ)法。

模塊寫法:

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

UMD

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

可以考慮用 UMD(Universal Module Definition),它能夠同時(shí)在 CommonJS、AMD 運(yùn)行,如果都不是,則會(huì)暴露到全局環(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ù)雜,就是先通過(guò)判斷不同模塊系統(tǒng)提供的全局變量是否存在,且類型符合預(yù)期,就能知道是 CommonJS 還是 requirejs,然后使用它們對(duì)應(yīng)的語(yǔ)法。都不匹配,那就暴露到全局。

雖然但是,UMD 無(wú)法支持 ES Modules,因?yàn)樗?import 不是變量,而是一個(gè)關(guān)鍵字,是編程語(yǔ)言層面的語(yǔ)法,在其他模塊系統(tǒng)中存在會(huì)報(bào)錯(cuò),且 import 只能在模塊最外邊使用。

結(jié)尾

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

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

相關(guān)文章

最新評(píng)論