淺析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)文章
梳理總結(jié)JavaScript的23個(gè)String方法
文章主要介紹了梳理總結(jié)JavaScript的23個(gè)String方法,JavaScript?中的String類型用于表示文本型的數(shù)據(jù)。它是由無(wú)符號(hào)整數(shù)值作為元素而組成的集合,更多詳細(xì)內(nèi)容需要的朋友可以參考一下2022-07-07js下拉框二級(jí)關(guān)聯(lián)菜單效果代碼具體實(shí)現(xiàn)
這篇文章介紹了js下拉框二級(jí)關(guān)聯(lián)菜單效果代碼具體實(shí)現(xiàn),有需要的朋友可以參考一下2013-08-08Javacript實(shí)現(xiàn)顏色梯度變化和漸變的效果代碼
用js對(duì)導(dǎo)航欄的顏色做了梯度的變化處理,通過(guò)處理..獲取兩種顏色在變化時(shí)的各種顏色字符串,并且字符串的個(gè)數(shù),即獲取的頻率可以調(diào)節(jié)2013-05-05微信小程序五子棋游戲的悔棋實(shí)現(xiàn)方法【附demo源碼下載】
這篇文章主要介紹了微信小程序五子棋游戲的悔棋實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了微信小程序中悔棋的原理與相關(guān)實(shí)現(xiàn)技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2019-02-02把json格式的字符串轉(zhuǎn)換成javascript對(duì)象或數(shù)組的方法總結(jié)
下面小編就為大家?guī)?lái)一篇把json格式的字符串轉(zhuǎn)換成javascript對(duì)象或數(shù)組的方法總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11ES6新特性八:async函數(shù)用法實(shí)例詳解
這篇文章主要介紹了ES6新特性八:async函數(shù)用法,結(jié)合實(shí)例形式分析了async函數(shù)的功能、原理、使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-04-04純JS實(shí)現(xiàn)可用于頁(yè)碼更換的飛頁(yè)特效示例
這篇文章主要介紹了純JS實(shí)現(xiàn)可用于頁(yè)碼更換的飛頁(yè)特效,涉及javascript結(jié)合時(shí)間函數(shù)的數(shù)學(xué)運(yùn)算與頁(yè)面元素屬性動(dòng)態(tài)修改相關(guān)操作技巧,需要的朋友可以參考下2018-05-05js數(shù)字滑動(dòng)時(shí)鐘的簡(jiǎn)單實(shí)現(xiàn)(示例講解)
下面小編就為大家?guī)?lái)一篇js數(shù)字滑動(dòng)時(shí)鐘的簡(jiǎn)單實(shí)現(xiàn)(示例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08