模塊載入過程: 路徑分析 文件定位 模塊編譯 Node對引入過的模塊都會進行緩存,以減少二次引入時的開銷。緩存的是編譯和執行之後的對象。require時對緩存中的模塊是第一優先順序的 路徑分析 模塊標識符:require的參數,按書寫形式可以分成以下幾類: 核心模塊:如http,fs,path 文件模 ...
模塊載入過程:
路徑分析 -> 文件定位 -> 模塊編譯
Node對引入過的模塊都會進行緩存,以減少二次引入時的開銷。緩存的是編譯和執行之後的對象。require時對緩存中的模塊是第一優先順序的
路徑分析
模塊標識符:require的參數,按書寫形式可以分成以下幾類:
- 核心模塊:如http,fs,path
- 文件模塊
- 路徑模塊
- 相對路徑模塊:.或..開始
- 絕對路徑模塊:/開始
- 非路徑形式的模塊
- 路徑模塊
核心模塊
- 在Node.js源代碼編譯過程中就已經編譯成二進位代碼,載入速度超快;
- 優先順序僅次於緩存,因此與核心模塊同標識符的文件模塊都不能載入成功。
路徑形式的文件模塊
- require時才會進行模塊編譯和執行;
- 會轉換為真實路徑,並且以真實路徑作為索引,將編譯執行後的對象放到緩存中。
非路徑形式的文件模塊
- 從當前模塊開始,一直沿路徑向上逐級遞歸,直到根目錄,尋找node_modules目錄進行文件定位;
文件定位
分析標識符的過程中,先分析文件擴展名,沒有查找到對應的文件,但是得到一個目錄,就會將該目錄當成一個包來處理。
文件擴展名分析
require
時的標識符不需要包含文件擴展名,Node會按.js,.json,.node
的次序同步定位。
判斷文件是否存在是同步進行的,所以.json
和.node
在引入時加上擴展名會加快引入速度
目錄分析和包
模塊編譯
文件模塊在定位成功後,Node會新建一個Module對象,然後根據路徑載入並編譯。根據文件的擴展名不同,其載入的方法也不同。
//Module 對象
function Module(id,parent){
this.id = id;
this.exports = {};
this.parent = parent;
updateChildren(parent, this, false);
this.filename = null;
this.loaded = false;
this.children = [];
}
可以通過require.extensions知道系統中已有的擴展方式。甚至可以使用require.extensions['.ext']的形式對.ext擴展名進行自定義載入方式,不過官方不鼓勵,建議先編譯成JavaScript文件。
.js 文件
通過fs模塊同步讀取文件後編譯執行;
首先會將文件內容進行頭尾包裝:
(function(exports , require , module , __filename , __dirname){
**JavaScript content**
});
這樣做可以:
- 在該文件中引入
exports , require , module , __filename , __dirname
變數; - 每個模塊文件之間都進行了作用域隔離;
包裝之後的模塊就會交給vm.runInThisContext執行得到一個function(註意是function)。最後將module.exports,require,module以及在文件定位中得到的 __filename 和 __dirname作為參數傳遞給這個function
執行。
所以嘛,在模塊內對exports進行賦值:
exports = function(){};
是改變了匿名函數的形參的引用,但是實參中無論是module還是module.exports都不能知道被賦值了。
.node
這是用C/C++編寫的擴展文件,通過dlopen()方法載入最後編譯生成的文件。
.json
通過fs模塊同步讀取文件後,用JSON.parse()解析返回結果,然後將它賦給模塊對象的exports。
每一個編譯成功的模塊都會將其文件路徑作為索引緩存在Module._cache對象上,以提高二次引入的性能。
引用:
http://nodejs.cn/
http://www.ituring.com.cn/book/1290