1.CommonJS
1.1 基本概念
- 為JS的表現來制定規範,因為js沒有子產品的功能是以CommonJS應運而生,它希望js可以在任何地方運作,不隻是浏覽器中。
- 同步
- CommonJS規範 通過model.exports定義的,在前端浏覽器中并不支援
- NodeJS是CommonJS規範的實作,webpack 也是以CommonJS的形式來書寫的。
- CommonJS定義的子產品分為: 子產品引用(require) ; 子產品定義(exports) ; 子產品辨別(module)
require()用來引入外部子產品;exports對象用于導出目前子產品的方法或變量,唯一的導出口;module對象就代表子產品本身。
var foo = require('foo.js');
var count = 1;
var plusCount = () => {
foo.add(count);
};
module.exports = {
count,
plusCount
};
浏覽器不相容CommonJS的根本原因,也正是在于缺少四個Node.js環境的變量。
- module
- exports
- require
- global
1.2 特點
- 對于基本資料類型,屬于複制。即會被子產品緩存。同時,在另一個子產品可以對該子產品輸出的變量重新指派。
- 對于複雜資料類型,屬于淺拷貝。由于兩個子產品引用的對象指向同一個記憶體空間,是以對該子產品的值做修改時會影響另一個子產品。
// b.js
let num = 1;
let obj = {
name: 'hcd'
};
setTimeout(() => {
console.log('b.js-num:', num);
console.log('b.js-name:', obj.name);
}, 1000)
module.exports = {
num,
obj
}
// a.js
let mod = require('./b.js')
mod.num = 2;
mod.obj.name = 'newName'
console.log('a.js-num:', mod.num);
console.log('a.js-name:', mod.obj.name);
//運作node
node a.js
a.js-num: 2
a.js-name: newName
b.js-num: 1 // 1秒後
b.js-name: newName // 1秒後
- 當使用require指令加載某個子產品時,就會運作整個子產品的代碼。
- 當使用require指令加載同一個子產品時,不會再執行該子產品,而是取到緩存之中的值。也就是說,CommonJS子產品無論加載多少次,都隻會在第一次加載時運作一次,以後再加載,就傳回第一次運作的結果,除非手動清除系統緩存。
- 循環加載時,屬于加載時執行。即腳本代碼在require的時候,就會全部執行。一旦出現某個子產品被”循環加載”,就隻輸出已經執行的部分,還未執行的部分不會輸出。
// a.js
exports.done = false
let b = require('./b.js')
console.log('a.js-1', b.done)
exports.done = true
console.log('a.js-2', '執行完畢')
// b.js
exports.done = false
let a = require('./a.js')
console.log('b.js-1', a.done)
exports.done = true
console.log('b.js-2', '執行完畢')
// c.js
let a = require('./a.js')
let b = require('./b.js')
console.log('c.js-1', '執行完畢', a.done, b.done)
運作node c.js
b.js-1 false
b.js-2 執行完畢
a.js-1 true
a.js-2 執行完畢
c.js-1 執行完畢 true true
說明:
- 在Node.js中執行c子產品。此時遇到require關鍵字,執行a.js中所有代碼。
- 在a子產品中exports之後,通過require引入了b子產品,執行b子產品的代碼。
- 在b子產品中exports之後,又require引入了a子產品,此時執行a子產品的代碼。
- 因為 某個子產品被”循環加載”,就隻輸出已經執行的部分,還未執行的部分不會輸出。a子產品隻執行exports.done =
- false這條語句。
- 回到b子產品,列印b.js-1, exports, b.js-2。b子產品執行完畢
- 回到a子產品,接着列印a.js-1, exports, b.js-2。a子產品執行完畢
- 回到c子產品,接着執行require,需要引入b子產品。由于在a子產品中已經引入過了,是以不會再執行該子產品,而是取到緩存之中的值,直接就可以輸出值了。 結束。
2.AMD
2.1誕生背景
基于commonJS規範的nodeJS出來以後,服務端的子產品概念已經形成,很自然地,大家就想要用戶端子產品。而且最好兩者能夠相容,一個子產品不用修改,在伺服器和浏覽器都可以運作。但是,由于一個重大的局限,使得CommonJS規範不适用于浏覽器環境。因為會有一個很大的問題:
var math = require('math');
math.add(2, 3);
第二行math.add(2, 3),在第一行require(‘math’)之後運作,是以必須等math.js加載完成。也就是說,如果加載時間很長,整個應用就會停在那裡等。您會注意到 require 是同步的。
這對伺服器端不是一個問題,因為所有的子產品都存放在本地硬碟,可以同步加載完成,等待時間就是硬碟的讀取時間。但是,對于浏覽器,這卻是一個大問題,因為子產品都放在伺服器端,等待時間取決于網速的快慢,可能要等很長時間,浏覽器處于”假死”狀态。
是以,浏覽器端的子產品,不能采用”同步加載”(synchronous),隻能采用”異步加載”(asynchronous)。這就是AMD規範誕生的背景。
CommonJS是主要為了JS在後端的表現制定的,他是不适合前端的,AMD(異步子產品定義)出現了,它就主要為前端JS的表現制定規範。
2.2AMD
AMD (異步子產品定義Asynchronous Module Definition) :是require.js在推廣過程中對子產品定義的規範化産出
- AMD:
子產品本身和子產品之間的引用可以被異步的加載,是一個概念
先引入的子產品,後使用的引用子產品的方法,是以我們稱之為依賴前置
- AMD優點:
包括異步的調用和本身的高擴充性,
它實作了解耦,子產品在代碼中也可通過識别号進行查找。
- require.js
是對AMD這個概念的實作,
require.js的誕生,就是為了解決這兩個問題:
實作js檔案的異步加載,避免網頁失去響應;
管理子產品之間的依賴性,便于代碼的編寫和維護。
- 官網:
http://www.requirejs.cn/
define(['./package/lib.js'], function(lib) {
function say(){
lib.log('this is fn');
}
return {
say:say
};
});
說明:
- ./package/lib.js使我們依賴的子產品,回調函數中參數lib表示的是引入的子產品的所有方法和屬性,我們可以調用
- 後來我們又在目前子產品定義了say方法,并return輸出
- 可以看到我們是先引入的子產品,後使用的引用子產品的方法,是以我們稱之為依賴前置
3. CMD
Cmd是SeaJs在推廣過程中對子產品定義的規範化産出
Cmd: 同步子產品定義,是一個概念
SeaJs: SeaJS的作者是前淘寶UED,現支付寶前端工程師玉伯。
原則: 依賴就近原則
// 所有的子產品通過define定義
define(function (require, exports, module) {
//通過require引入依賴,并不是AMD的前置依賴,而是依賴就近原則,在哪裡使用,在哪裡引入,就是同步的概念,即用即傳回
var $ = require('jquery');
//輸出子產品中定義的方法
exports.sayHello = function () {
$('#hello').toggle('slow');
};
});
4. AMD和CMD的差別
AMD 通過 require.js實作
CMD 通過 sea.js實作
相同處 :
RequireJS 和 Sea.js 都是子產品加載器,倡導子產品化開發理念,核心價值是讓 JavaScript 的子產品化開發變得簡單自然。
不同之處 :
- 定位有差異。RequireJS 想成為浏覽器端的子產品加載器,同時也想成為 Rhino / Node 等環境的子產品加載器。Sea.js則專注于 Web 浏覽器端,同時通過 Node 擴充的方式可以很友善跑在 Node 環境中。
- 遵循的規範不同。RequireJS 遵循 AMD(異步子產品定義)規範,Sea.js 遵循 CMD(通用子產品定義)規範。規範的不同,導緻了兩者 API 不同。Sea.js 更貼近 CommonJS Modules/1.1 和 Node Modules 規範。
- RequireJS 是依賴前置,Sea.js是依賴就近
- 對開發調試的支援有差異。Sea.js 非常關注代碼的開發調試,有 nocache、debug 等用于調試的插件。RequireJS無這方面的明顯支援。
- 插件機制不同。RequireJS 采取的是在源碼中預留接口的形式,插件類型比較單一。Sea.js 采取的是通用事件機制,插件類型更豐富。
總之,如果說 RequireJS 是 Prototype 類庫的話,則 Sea.js 緻力于成為 jQuery 類庫。
5. ES6
上面的AMD,CMD,CommonJs都是ES5時期的。
ES6中無需引入别的js檔案,ES6 在語言标準的層面上,實作了子產品功能,而且實作得相當簡單,完全可以取代 CommonJS 和 AMD 規範,成為浏覽器和伺服器通用的子產品解決方案。
ES6 子產品的設計思想是盡量的靜态化,使得編譯時就能确定子產品的依賴關系,以及輸入和輸出的變量。CommonJS 和 AMD 子產品,都隻能在運作時确定這些東西。
我這裡隻是簡單說一下,推薦阮一峰老師的學習網站,裡面有非常詳細的講解:
http://es6.ruanyifeng.com/#docs/module
————————————————
版權聲明:本文為CSDN部落客「南張門」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/haochangdi123/article/details/80408874