標籤:style blog c ext http a
檔案尋找流程圖
從檔案模組緩衝中載入
儘管原生模組與檔案模組的優先順序不同,但是都不會優先於從檔案模組的緩衝中載入已經存在的模組。
從原生模組載入
原生模組的優先順序僅次於檔案模組緩衝的優先順序。require方法在解析檔案名稱之後,優先檢查模組是否在原生模組列表中。以http模組為例,儘管在目錄下存在一個http/http.js/http.node/http.json檔案,require(“http”)都不會從這些檔案中載入,而是從原生模組中載入。 原生模組也有一個緩衝區,同樣也是優先從緩衝區載入。如果緩衝區沒有被載入過,則調用原生模組的載入方式進行載入和執行。
從檔案載入
當檔案模組緩衝中不存在,而且不是原生模組的時候,Node.js會解析require方法傳入的參數,並從檔案系統中載入實際的檔案,載入過程中的封裝和編譯細節在前一節中已經介紹過,這裡我們將詳細描述尋找檔案模組的過程,其中,也有一些細節值得知曉。 require方法接受以下幾種參數的傳遞:
- http、fs、path等,原生模組。
- ./mod或../mod,相對路徑的檔案模組。
- /pathtomodule/mod,絕對路徑的檔案模組。
- mod,非原生模組的檔案模組。
在進入路徑尋找之前有必要描述一下module path這個Node.js中的概念。對於每一個被載入的檔案模組,建立這個模組對象的時候,這個模組便會有一個paths屬性,其值根據當前檔案的路徑計算得到。我們建立modulepath.js這樣一個檔案,其內容為:
console.log(module.paths); |
我們將其放到任意一個目錄中執行node modulepath.js命令,將得到以下的輸出結果。
[ ‘/home/jackson/research/node_modules‘, ‘/home/jackson/node_modules‘, ‘/home/node_modules‘, ‘/node_modules‘ ] |
Windows下:
[ ‘c:\\nodejs\\node_modules‘, ‘c:\\node_modules‘ ] |
可以看出module path的建置規則為:從當前檔案目錄開始尋找node_modules目錄;然後依次進入父目錄,尋找父目錄下的node_modules目錄;依次迭代,直到根目錄下的node_modules目錄。 除此之外還有一個全域module path,是當前node執行檔案的相對目錄(../../lib/node)。如果在環境變數中設定了HOME目錄和NODE_PATH目錄的話,整個路徑還包含NODE_PATH和HOME目錄下的.node_libraries與.node_modules。其最終值大致如下:
[NODE_PATH,HOME/.node_modules,HOME/.node_libraries,execPath/../../lib/node] |
require方法中的檔案尋找策略
由於Node.js中存在4類別模組(原生模組和3種檔案模組),儘管require方法極其簡單,但是內部的載入卻是十分複雜的,其載入優先順序也各自不同。 簡而言之,如果require絕對路徑的檔案,尋找時不會去遍曆每一個node_modules目錄,其速度最快。其餘流程如下:
- 從module path數組中取出第一個目錄作為尋找基準。
- 直接從目錄中尋找該檔案,如果存在,則結束尋找。如果不存在,則進行下一條尋找。
- 嘗試添加.js、.json、.node尾碼後尋找,如果存在檔案,則結束尋找。如果不存在,則進行下一條。
- 嘗試將require的參數作為一個包來進行尋找,讀取目錄下的package.json檔案,取得main參數指定的檔案。
- 嘗試尋找該檔案,如果存在,則結束尋找。如果不存在,則進行第3條尋找。
- 如果繼續失敗,則取出module path數組中的下一個目錄作為基準尋找,迴圈第1至5個步驟。
- 如果繼續失敗,迴圈第1至6個步驟,直到module path中的最後一個值。
- 如果仍然失敗,則拋出異常。
整個尋找過程十分類似原型鏈的尋找和範圍的尋找。所幸Node.js對路徑尋找實現了緩衝機制,否則由於每次判斷路徑都是同步阻塞式進行,會導致嚴重的效能消耗。