javascript
commonjs是什么_JavaScript模块化标准CommonJS/AMD/CMD/UMD/ES6Module的区别
JS-模塊化進程
隨著js技術的不斷發展,途中會遇到各種問題,比如模塊化。
那什么是模塊化呢,他們的目的是什么?
定義:如何把一段代碼封裝成一個有用的單元,以及如何注冊此模塊的能力、輸出的值依賴引用:如何引用其它代碼單元
到目前為止,大概分為以下幾個里程碑式階段。
原始的開發方式 ---> CommonJS ---> AMD ---> CMD ---> UMD ---> ES6Module原始的開發方式
最開始的時候,JS自身是沒有模塊機制的。項目有多個js文件。
// a.jsfunction foo() {}// b.jsfunction bar() {}// c.jsfoo()HTML加載
原始的開發方式,隨著項目的復雜度,代碼量越來越多,所需要加載的文件也越來越多,這個時候,就要考慮幾個問題了:
- 命名問題:所有文件的方法都是掛載到global上,會污染全局環境,并且需要考慮命名沖突問題。
- 依賴問題:script是順序加載的,如果各文件之間有依賴,那我們得考慮加載.js文件的書寫順序。
- 網絡問題。如果文件過多,所需請求次數會增多,增加加載時間。
CommonJS && node.js
CommonJS規范,主要運行于服務器端,同步加載模塊,而加載的文件資源大多數在本地服務器,所以執行速度或時間沒問題。Node.js很好的實現了該規范。該規范指出,一個單獨的文件就是一個模塊。模塊功能主要的幾個命令:require和module.exports。require命令用于輸入其他模塊提供的功能,module.exports命令用于規范模塊的對外接口,輸出的是一個值的拷貝,輸出之后就不能改變了,會緩存起來。
// moduleA.jsvar name = 'Drex'function foo() {}module.exports = exports = { name, foo}// moduleB.jsvar ma = require('./moduleA') // 可以省略后綴.jsexports.bar = function() { ma.name === 'Drex' // true ma.foo() // 執行foo方法}// moduleC.jsvar mb = require('./moduleB')mb.bar()通過例子,我們可以看出require(moduleId)來加載其他模塊的內容,其返回值就是其引用的外部模塊所暴露的API,之后再通過module.exports或者exports來為當前模塊的方法和變量提供輸出接口。
最后通過node來執行模塊。
AMD && Require.js
AMD(Asynchronous Module Definition - 異步加載模塊定義)規范,制定了定義模塊的規則,一個單獨的文件就是一個模塊,模塊和模塊的依賴可以被異步加載。主要運行于瀏覽器端,這和瀏覽器的異步加載模塊的環境剛好適應,它不會影響后面語句的運行。該規范是在RequireJs的推廣過程中逐漸完善的。
模塊功能主要的幾個命令:define、require、return和define.amd。define是全局函數,用來定義模塊,define(id?, dependencies?, factory)。require命令用于輸入其他模塊提供的功能,return命令用于規范模塊的對外接口,define.amd屬性是一個對象,此屬性的存在來表明函數遵循AMD規范。
// moduleA.jsdefine(['jQuery','lodash'], function($, _) { var name = 'Drex', function foo() {} return { name, foo }})// index.jsrequire(['moduleA'], function(a) {a.name === 'Drex' // truea.foo() // 執行A模塊中的foo函數// do sth...})// index.html在這里,我們使用define來定義模塊,return來輸出接口, require來加載模塊,這是AMD官方推薦用法。當然也可以使用其他兼容性的寫法,比如對 Simplified CommonJS Wrapper 格式的支持,但背后還是原始AMD的運行邏輯。AMD的運行邏輯是:提前加載,提前執行。在Requirejs中,申明依賴模塊時,會第一時間加載并執行模塊內的代碼,使后面的回調函數能在所需的環境中運行。為了更好地優化請求,同時推出了打包工具r.js,使所需加載的文件數減少。require.js模塊化開發,并用r.js打包例子
CMD && Sea.js
CMD(Common Module Definition - 通用模塊定義)規范主要是Sea.js推廣中形成的,一個文件就是一個模塊,可以像Node.js一般書寫模塊代碼。主要在瀏覽器中運行,當然也可以在Node.js中運行。
// moduleA.js// 定義模塊define(function(require, exports, module) { var func = function() { var a = require('./a') // 到此才會加載a模塊 a.func() if(false) { var b = require('./b') // 到此才會加載b模塊 b.func() } } // do sth... exports.func = func;})// index.js// 加載使用模塊seajs.use('moduleA.js', function(ma) {var ma = math.func()})// HTML,需要在頁面中引入sea.js文件。這里define是一個全局函數,用來定義模塊,并通過exports向外提供接口。之后,如果要使用某模塊,可以通過require來獲取該模塊提供的接口。最后使用某個組件的時候,通過seajs.use()來調用。
通過exports暴露接口。這意味著不需要命名空間了,更不需要全局變量。通過require引入依賴。這可以讓依賴內置,我們只需要關心當前模塊的依賴。關注度分離CMD推崇依賴就近,延遲執行。在上面例子中,通過require引入的模塊,只有當程序運行到此處的時候,模塊才會自動加載執行。
同時推出了spm(static package manager)的打包方式,聽說支付寶的項目在使用。
UMD && webpack
UMD(Universal Module Definition - 通用模塊定義)模式,該模式主要用來解決CommonJS模式和AMD模式代碼不能通用的問題,并同時還支持老式的全局變量規范。
// 使用Node, AMD 或 browser globals 模式創建模塊(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD模式. 注冊為一個匿名函數 define(['b'], factory); } else if (typeof module === 'object' && module.exports) { // Node等類CommonJS的環境 module.exports = factory(require('b')); } else { // 瀏覽器全局變量 (root is window) root.returnExports = factory(root.b); }}(typeof self !== 'undefined' ? self : this, function (b) { // 以某種方式使用 b //返回一個值來定義模塊導出。(即可以返回對象,也可以返回函數) return {};}));判斷define為函數,并且是否存在define.amd,來判斷是否為AMD規范,判斷module是否為一個對象,并且是否存在module.exports來判斷是否為CommonJS規范如果以上兩種都沒有,設定為原始的代碼規范。這種模式,通常會在webpack打包的時候用到。output.libraryTarget將模塊以哪種規范的文件輸出。
ES6 Module && ES6
在ECMAScript 2015版本出來之后,確定了一種新的模塊加載方式,我們稱之為ES6 Module。它和前幾種方式有區別和相同點。
它因為是標準,所以未來很多瀏覽器會支持,可以很方便的在瀏覽器中使用。它同時兼容在node環境下運行。模塊的導入導出,通過import和export來確定。可以和Commonjs模塊混合使用。CommonJS輸出的是一個值的拷貝。ES6模塊輸出的是值的引用,加載的時候會做靜態優化。CommonJS模塊是運行時加載確定輸出接口,ES6模塊是編譯時確定輸出接口。ES6模塊功能主要由兩個命令構成:import和export。import命令用于輸入其他模塊提供的功能。export命令用于規范模塊的對外接口。
export的幾種用法
// 輸出變量export var name = 'Drex'export var year = '2020'// 輸出一個對象(推薦)var name = 'Drex'var year = '2020'export { name, year}// 輸出函數或類export function add(a, b) {return a + b;}// export default 命令export default function() {console.log('foo')}import導入其他模塊// 正常命令import { name, year } from './module.js' //后綴.js不能省略// 如果遇到export default命令導出的模塊import ed from './export-default.js'模塊編輯好之后,它可以以多種形式加載。
1. 瀏覽器加載
瀏覽器加載ES6模塊,使用
也可以內嵌在網頁中
對于加載外部模塊,需要注意:
代碼是在模塊作用域之中運行,而不是在全局作用域運行。模塊內部的頂層變量,外部不可見。模塊腳本自動采用嚴格模式,不管有沒有聲明use strict。模塊之中,可以使用import命令加載其他模塊(.js后綴不可省略,需要提供絕對 URL 或相對 URL),也可以使用export命令輸出對外接口。模塊之中,頂層的this關鍵字返回undefined,而不是指向window。也就是說,在模塊頂層使用this關鍵字,是無意義的。同一個模塊如果加載多次,將只執行一次。
2. Node加載
Node要求 ES6 模塊采用.mjs后綴文件名。也就是說,只要腳本文件里面使用import或者export命令,就必須采用.mjs后綴名。這個功能還在試驗階段。安裝Node V8.5.0或以上版本,要用--experimental-modules參數才能打開該功能。
$ node --experimental-modules my-app.mjsNode的import命令只支持異步加載本地模塊(file:協議),不支持加載遠程模塊。
總結
以上是生活随笔為你收集整理的commonjs是什么_JavaScript模块化标准CommonJS/AMD/CMD/UMD/ES6Module的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React Native Android
- 下一篇: 黑森林宝箱位置