require參數類型
- http、fs、path等,原生模塊
- ./mod或../mod,相對路徑的文件模塊
- /pathtomodule/mod,絕對路徑的文件模塊
- mod,非原生模塊的文件模塊
在進入路徑查找之前有必要描述一下module path這個Node.js中的概念。對於每一個被加載的文件模塊,創建這個模塊對象的時候,這個模塊便會有一個paths屬性,其值根據當前文件的路徑計算得到。Node.js在編譯js文件的過程中實際完成的步驟有對js文件內容進行頭尾包裝。以app.js為例,我們將其放在任意一個文件夾中,包裝之后的app.js將會變成以下形式:
在app.js中輸入如下內容:
執行node module app.js命令,將得到以下的輸出結果:
Windows下:
可以看出module path的生成規則為:從當前文件目錄開始查找node_modules目錄;然后依次進入父目錄,查找父目錄下的node_modules目錄;依次迭代,直到根目錄下的node_modules目錄。
除此之外還有一個全局module path,是當前node執行文件的相對目錄(../../lib/node)。如果在環境變量中設置了HOME目錄和NODE_PATH目錄的話,整個路徑還包含NODE_PATH和HOME目錄下的.node_libraries與.node_modules。其最終值大致如下:
require方法中的文件查找策略(指內置模塊和第三方模塊):
1、從module path數組中取出第一個目錄作為查找基准。
2、直接從目錄中查找該文件,如果存在,則結束查找。如果不存在,則進行下一條查找。
3、嘗試添加.js、.json、.node后綴后查找,如果存在文件,則結束查找。如果不存在,則進行下一條。
4、嘗試將require的參數作為一個包來進行查找,讀取目錄下的package.json文件,取得main參數指定的文件。
5、嘗試查找該文件,如果存在,則結束查找。如果不存在,則進行第3條查找。
6、如果繼續失敗,則取出module path數組中的下一個目錄作為基准查找,循環第1至5個步驟。
7、如果繼續失敗,循環第1至6個步驟,直到module path中的最后一個值。
8、如果仍然失敗,則拋出異常。
文件查找流程:
由於 Node.js 中存在 4 類模塊(原生模塊和3種文件模塊),盡管 require 方法極其簡單,但是內部的加載卻是十分復雜的,其加載優先級也各自不同。如下圖所示:
一、優先從文件模塊緩存中加載
盡管原生模塊與文件模塊的優先級不同,但是都會優先從文件模塊的緩存中加載已經存在的模塊。
二、原生模塊
原生模塊的優先級僅次於文件模塊緩存的優先級。require 方法在解析文件名之后,優先檢查模塊是否在原生模塊列表中。
在實際開發過程中,如果你的文件和核心庫文件同名,加載過程中是會直接忽略你的項目文件。
例如在server下有config.json,require("config")。
require具體的加載過程是:
(1)核心庫中如果有config.js 會直接加載 忽略你的文件。
(2)如果沒有會檢查你的項目中是否有這個.js文件。
(3)如果沒有才會去查詢 .json 文件。
所以在開發過程中最好使用其相對路徑指明具體文件。
當第三方的模塊和內置模塊同名時,內置模塊將覆蓋第三方同名模塊。因此命名時需要注意不要和內置模塊同名。
原生模塊也有一個緩存區,同樣也是優先從緩存區加載。如果緩存區沒有被加載過,則調用原生模塊的加載方式進行加載和執行。
原生模塊的本質也是文件,原生模塊文件已經被編譯到了二進制文件中了,我們只需要按照名字來加載就可以了。如:
1、require(‘fs’)
2、require(‘http’)
三、路徑形式的模塊
我們說的路徑形式的模塊,其實就是加載自己寫的JS文件,有四種方式可以加載
當文件模塊緩存中不存在,而且不是原生模塊的時候,Node.js 會解析 require 方法傳入的參數,並從文件系統中加載實際的文件 。
注意,這里忽略了擴展名“.js”,以下是對等的:
如果當前目錄有my_mod.js和my_mod.json,則會優先加載 my_mod.js。
可以直接require一個目錄,假設有一個目錄名為folder,如:
此時,Node將搜索整個folder目錄,Node會假設folder為一個包並試圖找到包定義文件package.json。如果folder 目錄里沒有包含package.json文件,Node會假設默認主文件為index.js,即會加載index.js。如果index.js也不存在, 那么加載將失敗。
假如目錄結構如下:
package.json定義如下:
此時 require('./folder') 將返回模塊modA.js。如果package.json不存在,那么將返回模塊index.js。如果index.js也不存在,那么將發生載入異常。
如果foder同級目錄還有folder.js和folder.json,同時folder目錄下還有index.js,則require('./folder') 將返回folder.js中的內容,否則返回folder.json中的內容,否則會返回folder目錄下index.js中的內容,最后才會返回modA.js中的內容。
四、第三方模塊
凡是用到第三方模塊,都必須通過 npm 來下載;
使用的時候就可以通過 require(‘包名’) 的方式來進行加載才可以使用;
不可能有任何一個第三方包和核心模塊的名字是一樣的。
既不是核心模塊、也不是路徑形式的模塊,就是第三方模塊。
如果模塊名不是路徑,也不是內置模塊,Node將試圖去當前目錄的node_modules文件夾里搜索。如果當前目錄的node_modules里沒有找到,Node會從父目錄的node_modules里搜索,這樣遞歸下去直到根目錄。
總結:
其實主要就是兩種情況:
1、如果require中是名稱,則說明搜索的是內置模塊或者第三方模塊,此時內置模塊的優先級高於第三方模塊;
2、如果require中是路徑,則會按照自定義模塊的規則查找。