您好,登錄后才能下訂單哦!
本篇內容主要講解“JavaScript的CommonJS、AMD、CMD、ES6實例分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“JavaScript的CommonJS、AMD、CMD、ES6實例分析”吧!
AMD、CMD、CommonJs
是ES5
中提供的模塊化編程方案,import/export
是ES6
中新增的模塊化編程方案。
那么,究竟什么什么是AMD、CMD、CommonJs
?他們之間又存在什么區別呢?項目開發應該選用哪種模塊化編程規范,又是如何使用?本篇博文將一一解答以上疑問。
AMD
是”Asynchronous Module Definition
”的縮寫,即”異步模塊定義”。它采用異步方式加載模塊,模塊的加載不影響它后面語句的運行。
這里異步指的是不堵塞瀏覽器其他任務(dom
構建,css
渲染等),而加載內部是同步的(加載完模塊后立即執行回調)。
RequireJS
:是一個AMD
框架,可以異步加載JS
文件,按照模塊加載方法,通過define()函數定義,第一個參數是一個數組,里面定義一些需要依賴的包,第二個參數是一個回調函數,通過變量來引用模塊里面的方法,最后通過return來輸出。
AMD
是RequireJS
在推廣過程中對模塊定義的規范化產出,它是一個概念,RequireJS
是對這個概念的實現,就好比JavaScript
語言是對ECMAScript
規范的實現。AMD
是一個組織,RequireJS
是在這個組織下自定義的一套腳本語言。
不同于CommonJS
,它要求兩個參數:
require([module], callback);
第一個參數[module]
,是一個數組,里面的成員是要加載的模塊,callback
是加載完成后的回調函數。如果將上述的代碼改成AMD
方式:
require(['math'], function(math) { math.add(2, 3);})
其中,回調函數中參數對應數組中的成員(模塊)。
requireJS
加載模塊,采用的是AMD
規范。也就是說,模塊必須按照AMD
規定的方式來寫。
具體來說,就是模塊書寫必須使用特定的define()
函數來定義。如果一個模塊不依賴其他模塊,那么可以直接寫在define()
函數之中。
define(id, dependencies, factory);
id
:模塊的名字,如果沒有提供該參數,模塊的名字應該默認為模塊加載器請求的指定腳本名字;
dependencies
:模塊的依賴,已被模塊定義的模塊標識的數組字面量。依賴參數是可選的,如果忽略此參數,它應該默認為["require", "exports", "module"]
。然而,如果工廠方法的長度屬性小于3,加載器會選擇以函數的長度屬性指定的參數個數調用工廠方法。
factory
:模塊的工廠函數,模塊初始化要執行的函數或對象。如果為函數,它應該只被執行一次。如果是對象,此對象應該為模塊的輸出值。
假定現在有一個math.js
文件,定義了一個math
模塊。那么,math.js
書寫方式如下:
// math.jsdefine(function() { var add = function(x, y) { return x + y; } return { add: add }})
加載方法如下:
// main.jsrequire(['math'], function(math) { alert(math.add(1, 1));})
如果math
模塊還依賴其他模塊,寫法如下:
// math.jsdefine(['dependenceModule'], function(dependenceModule) { // ...})
當require()
函數加載math
模塊的時候,就會先加載dependenceModule
模塊。當有多個依賴時,就將所有的依賴都寫在define()
函數第一個參數數組中,所以說AMD
是依賴前置的。這不同于CMD
規范,它是依賴就近的。
CMD
CMD
即Common Module Definition
通用模塊定義,是SeaJS
在推廣過程中對模塊定義的規范化產出,是一個同步模塊定義,是SeaJS
的一個標準,SeaJS
是CMD
概念的一個實現,SeaJS
是淘寶團隊玉伯提供的一個模塊開發的js
框架。CMD
規范是國內發展出來的,就像AMD
有個requireJS
,CMD
有個瀏覽器的實現SeaJS
,SeaJS
要解決的問題和requireJS
一樣,只不過在模塊定義方式和模塊加載(可以說運行、解析)時機上有所不同。
CMD
通過define()
定義,沒有依賴前置,通過require
加載jQuery
插件,CMD
是依賴就近,在什么地方使用到插件就在什么地方require
該插件,即用即返,這是一個同步的概念。
在 CMD
規范中,一個模塊就是一個文件。代碼的書寫格式如下:
define(function(require, exports, module) { // 模塊代碼});
其中,
require
是可以把其他模塊導入進來的一個參數;
exports
是可以把模塊內的一些屬性和方法導出的;
module
是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法。
AMD
是依賴關系前置,在定義模塊的時候就要聲明其依賴的模塊;CMD
是按需加載依賴就近,只有在用到某個模塊的時候再去require
,示例代碼如下:
// CMDdefine(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴可以就近書寫 b.doSomething() // ... })// AMD 默認推薦的是define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() // 此處略去 100 行 b.doSomething() ...})
CommonJS
規范是通過module.exports
定義的,在前端瀏覽器里面并不支持module.exports
,通過node.js
后端使用。Nodejs
端使用CommonJS
規范,前端瀏覽器一般使用AMD
、CMD
、ES6
等定義模塊化開發規范。
CommonJS
的終極目標是提供一個類似Python
,Ruby
和Java
的標準庫。這樣的話,開發者可以使用CommonJS API
編寫應用程序,然后這些應用就可以運行在不同的JavaScript
解釋器和不同的主機環境中。
在兼容CommonJS
的系統中,你可以使用JavaScript
開發以下程序:
服務器端
JavaScript
應用程序;命令行工具;
圖形界面應用程序;
混合應用程序(如,Titanium或Adobe AIR);
2009年,美國程序員Ryan Dahl創造了node.js
項目,將javascript
語言用于服務器端編程。這標志"Javascript
模塊化編程"正式誕生。NodeJS
是CommonJS
規范的實現,webpack
也是以CommonJS
的形式來書寫。
node.js
的模塊系統,就是參照CommonJS
規范實現的。在CommonJS
中,有一個全局性方法require()
,用于加載模塊。假定有一個數學模塊math.js
,就可以像下面這樣加載。
var math = require('math');
然后,就可以調用模塊提供的方法:
var math = require('math');math.add(2,3); // 5
CommonJS
定義的模塊分為:模塊引用(require)、 模塊定義(exports)、模塊標識(module)。
其中,
require()
用來引入外部模塊;
exports
對象用于導出當前模塊的方法或變量,唯一的導出口;
module
對象就代表模塊本身。
雖說NodeJS
遵循CommonJS
的規范,但是相比也是做了一些取舍,添了一些新東西的。
NPM
作為Node
包管理器,同樣遵循CommonJS
規范。
下面講講commonJS
的原理以及簡易實現:
1、原理
瀏覽器不兼容CommonJS
的根本原因,在于缺少四個Node.js
環境變量。
module exports require global
只要能夠提供這四個變量,瀏覽器就能加載 CommonJS
模塊。
下面是一個簡單的示例。
var module = { exports: {}};(function(module, exports) { exports.multiply = function (n) { return n * 1000 }; }(module, module.exports))var f = module.exports.multiply; f(5) // 5000
上面代碼向一個立即執行函數提供 module 和 exports 兩個外部變量,模塊就放在這個立即執行函數里面。模塊的輸出值放在 module.exports 之中,這樣就實現了模塊的加載。
2、Browserify 的實現Browserify
是目前最常用的 CommonJS
格式轉換工具。
請看一個例子,main.js
模塊加載 foo.js
模塊。
// foo.jsmodule.exports = function(x) { console.log(x);};// main.jsvar foo = require("./foo");foo("Hi");
使用下面的命令,就能將main.js
轉為瀏覽器可用的格式。
$ browserify main.js > compiled.js
其中,Browserify
到底做了什么?安裝一下browser-unpack
,就清楚了。
$ npm install browser-unpack -g
然后,將前面生成的compile.js解包。
$ browser-unpack < compiled.js
[ { "id":1, "source":"module.exports = function(x) {\n console.log(x);\n};", "deps":{} }, { "id":2, "source":"var foo = require(\"./foo\");\nfoo(\"Hi\");", "deps":{"./foo":1}, "entry":true }]
可以看到,browerify
將所有模塊放入一個數組,id
屬性是模塊的編號,source
屬性是模塊的源碼,deps
屬性是模塊的依賴。
因為 main.js
里面加載了 foo.js
,所以 deps
屬性就指定 ./foo
對應1號模塊。執行的時候,瀏覽器遇到 require('./foo')
語句,就自動執行1號模塊的 source
屬性,并將執行后的 module.exports
屬性值輸出。
有關es6
模塊特性,強烈推薦阮一峰老師的:ECMAScript 6 入門 - Module 的語法專欄。
要說 ES6
模塊特性,那么就先說說 ES6
模塊跟 CommonJS
模塊的不同之處。
ES6
模塊輸出的是值的引用,輸出接口動態綁定,而CommonJS
輸出的是值的拷貝;
ES6
模塊編譯時執行,而CommonJS
模塊總是在運行時加載。
CommonJS
模塊輸出的是值的拷貝(原始值的拷貝),也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。
// a.jsvar b = require('./b');console.log(b.foo);setTimeout(() => { console.log(b.foo); console.log(require('./b').foo);}, 1000);// b.jslet foo = 1;setTimeout(() => { foo = 2;}, 500);module.exports = { foo: foo,};// 執行:node a.js// 執行結果:// 1// 1// 1
上面代碼說明,b 模塊加載以后,它的內部 foo 變化就影響不到輸出的 exports.foo 了。這是因為 foo 是一個原始類型的值,會被緩存。所以如果你想要在 CommonJS
中動態獲取模塊中的值,那么就需要借助于函數延時執行的特性。
// a.jsvar b = require('./b');console.log(b.foo);setTimeout(() => { console.log(b.foo); console.log(require('./b').foo);}, 1000);// b.jsmodule.exports.foo = 1; // 同 exports.foo = 1 setTimeout(() => { module.exports.foo = 2;}, 500);// 執行:node a.js// 執行結果:// 1// 2// 2
所以我們可以總結一下:
CommonJS
模塊重復引入的模塊并不會重復執行,再次獲取模塊直接獲得暴露的module.exports
對象。如果你需要處處獲取到模塊內的最新值的話,也可以每次更新數據的時候每次都要去更新
module.exports
上的值如果暴露的
module.exports
的屬性是個對象,那就不存在這個問題了。
到此,相信大家對“JavaScript的CommonJS、AMD、CMD、ES6實例分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。