1. CommonJS模塊規范
1.1 模塊參考
var math = require('math');
1.2 模塊定義
[!NOTE]
背景關系提供exports物件用于匯出當前模塊的方法和變數,并且他是唯一的匯出出口
exports實際上是module.exports,而module.exports就是以一個暴露給外部的物件,
- exports.some就是給這個物件上添加屬性
- 直接使用 module.exports = {...} 則可以讓外部直接獲取到這個物件,相當與為exports換了一個參考,如果在這之前使用exports.some會把之前的覆寫
1.3 CommonJS 用法
// a.js
module.exports = {
a: 1
}
// or
exports.a = 1
// b.js
var module = require('./a.js')
module.a // -> log 1
1.4 原理
var module = require('./a.js')
module.a
// 這里其實就是包裝了一層立即執行函式,這樣就不會污染全域變數了,
// 重要的是 module 這里,module 是 Node 獨有的一個變數
module.exports = {
a: 1
}
// module 基本實作
var module = {
id: 'xxxx', // 我總得知道怎么去找到他吧
exports: {} // exports 就是個空物件
}
// 這個是為什么 exports 和 module.exports 用法相似的原因
var exports = module.exports
var load = function (module) {
// 匯出的東西
var a = 1
module.exports = a
return module.exports
};
// 然后當我 require 的時候去找到獨特的
// id,然后將要使用的東西用立即執行函式包裝下,over
2. Node的模塊實作
在Node中引入模塊,需要經歷3個步驟
- 路徑分析
- 檔案定位
- 編譯執行
在node中,模塊分為兩類:一類是node提供的模塊稱為核心模塊,一類是用戶撰寫的成為檔案模塊,
-
核心模塊在編譯中編譯成了二進制檔案,在Node行程啟動時,部分核心模塊就被直接加載入記憶體,所以這部分核心模塊引入時就省了檔案定位和編譯執行這兩個步驟,并且在路徑分析中優先判斷,它的加載速度是最快的,
-
檔案模塊是運行時動態加載,需要完整的路徑分析、檔案定位、編譯執行
2.1 優先從快取加載
Node對引入的模塊都回進行快取,而且快取的是編譯執行后的物件,
不管是核心模塊還是檔案模塊,require()都一律采用快取優先的方式,
2.2 路徑分析和檔案定位
2.2.1 模塊識別符號分析
- 核心模塊
- 路徑形式的檔案模塊
- 自定義模塊
- node_modules下
- 查找最費時
2.2.2 檔案定位
- 檔案拓展名分析
- 如果省略拓展名,回按 .js .node .json的次序依次嘗試
- 如果.node .json的話,加上拓展名會加快一點速度
- 同步配合快取,可大幅緩解單執行緒中阻塞式呼叫的缺陷
- 目錄分析和包
- 如果沒有檔案名,會將Index當作默認檔案名
2.3 模塊編譯
- .js檔案
- 通過fs同步讀取后編譯執行
- .node
- 這是用C/C++撰寫的拓展檔案,通過dlopen()方法加載最后編譯生成的檔案
- .json
- 用JSON.parse()決議回傳結果
- 其余拓展名
- 當作.js檔案處理
[!NOTE]
每一個編譯成功的模塊都會將其檔案路徑索引快取在Module._cache物件上,以提高二次引入性能
2.3.1 js模塊的編譯
[!NOTE]
在編譯的程序中,Node對獲取的JS檔案進行了頭尾包裝,這也是每個模塊都能訪問到 require、exports、module、__filename、__dirname的原因
(funciton(exports, require, module, __filename, __dirname) {
/* 自己寫的代碼 */
});
這樣使得模塊檔案間都進行了作用域隔離,不用擔心變數污染全域,
為moudle.exports賦值,exports物件是通過形參的方式傳入,直接賦值形參會改變形參的參考,但并不能改變作用域外的值,
exports = function() {
// my class
}
var change = function(a) {
a = 100;
}
var a = 10;
change(a);
console.log(a); // => 10
如果要達到require引入一個類的效果,請賦值給 module.exports物件,這個迂回的方案不改變形參的參考,
2.3.2 C/C++ 模塊的編譯
Node呼叫process.dlopen()方法進行加載和執行,
實際上 .node模塊并不需要編譯,因為它是撰寫C/C++模塊之后編譯生成的,所以這里只有加載和執行的程序,在執行的程序中,模塊exports物件與.node模塊產生練習,然后回傳給呼叫者,
3. 核心模塊
[!NOTE]
Node的核心模塊在編譯成可執行檔案的程序中被編譯進了二進制檔案,核心模塊其實分為C/C++撰寫的和Javascript撰寫的兩部分,其中C/C++檔案存放在Node專案的src目錄下,Javascript檔案存放在lib目錄下,
-
C/C++拓展模塊
-
模塊呼叫堆疊
-
前后端公用模塊
-
模塊側重點
-
前端瓶頸在于帶寬,后端瓶頸在于CPU和記憶體等資源,前端需要通過網路加載代碼,后端則從磁盤加載,二者加載速度不再同一量級上,
-
node的模塊引入幾乎都是同步的,但前端模塊若是也采用同步方式來引入必會在用戶體驗上造成很大的問題,即UI初始化實際過長
4. AMD規范
Asynchronous Moudle Definition “異步模塊定義”, AMD需要在宣告的時候指定所有的依賴,通過形參傳遞依賴到模塊內容中,
定義如下
define(id?, dependencies, factory);
5. CMD 規范
與AMD主要區別在于定于模塊與依賴引入部分,
CMD支持動態引入
define(funtion(require, exports, moudle) {
// The module code goes here
})
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/167482.html
標籤:JavaScript
