在nodejs中引進模組要經歷的步驟
在nodejs中引入模組需要經歷如下3個步驟
1.路徑分析
2.檔案定位
3.編譯執行
在nodejs中模組分為兩類,一類是nodejs提供的模組,稱為核心模組,另一類的使用者編寫的模組,稱為檔案模組。
1.核心模組在nodejs原始碼的編譯過程中,編譯進了二進位制執行檔案,在nodejs程序啟動時,部分核心模組就被直接載入進記憶體夠中 ,這部分核心模組引入時,檔案定位和編譯執行兩個步驟可以省略,並且在路徑分析中優先判斷,所以它的載入速度時最快的。
2.檔案模組則是在執行時動態載入,需要完整的路徑分析,檔案定位,編譯執行過程,速度比核心模組慢。
一.優先從快取中載入
為了減少二次引入的開銷,nodejs會對引入過的模組進行快取,快取的是編譯和執行之後的物件。不論是核心模組還是檔案模組,require()方法對相同模組的二次載入都一律採用快取優先的方式。不同之處在於核心模組的快取檢查優先於檔案模組的快取檢查。
二.路徑分析
在nodejs中使用require()引入檔案時,require()方法接受一個識別符號作為引數。nodejs就是通過這個識別符號進行模組查詢的。模組識別符號只要分為如下幾類:
1.核心模組,如http,fs,path等
2. .或..開始的相對路徑檔案模組
3.以/開始的絕對路徑檔案模組
4.非路徑形式的檔案模組,如自定義的connect模組
2.1.核心模組
核心模組的優先順序僅次於快取載入,如果要載入一個與核心模組識別符號相同的自定義模組,那是不會成功的。如,你自己編寫了一個http使用者模組,想要載入成功,就必須選擇一個不同的識別符號或者使用路徑的形式。
2.2.路徑形式的檔案模組
以..和/開始的識別符號都被當作檔案模組來處理。在分析路徑模組時,require()方法會將路徑轉化為真實路徑,並以真實路徑作為索引,將編譯執行後的結果存放在快取中,以使二次載入時更快。由於指定了確切的檔案位置,所以在查詢過程中可以節約大量時間,其載入速度慢於核心模組。
2.3自定義模組
自定義模組指非核心模組,也不是路徑形式的標誌符。它是一種特殊的檔案模組,可能是一個檔案或者包的形式。這類模組的查詢是最耗時的。
在介紹自定義模組的查詢方法之前,需要先介紹一下模組路徑這個概念。
模組路徑是nodejs在定位檔案模組的具體檔案時制定的查詢策略,其具體表現為一個路徑組成的陣列。例如:
(1)建立一個module_path.js檔案,其內容為console.log(module.path);
(2)將其放在任何一個目錄中,然後執行node module_path.js
在Linux下,你可能得到的時這樣一個數組輸出:
['/home/jack/res/node_modules',
'/home/jack/node_modules',
'/home/node_modules',
'/node_modules']
在windows下,可能如下:
['c:\\nodejs\\node_modules','c:\\node_modules']
可以看出,模組路徑的生成規則如下:
1.當前檔案目錄下的node_modules目錄
2.父目錄下node_modules目錄
3.父目錄的父目錄的node_modules目錄
4.沿路徑向上逐級遞迴,直到根目錄下的node_modules目錄
在載入的過程中,nodejs會逐個嘗試模組路徑中的路徑,直到找到目標檔案為止。所以當前檔案的路徑越深,模組查詢耗時就越多,這是自定義模組的載入速度最慢的原因
三.檔案定位
從快取載入的優化策略使得二次引入時不需要路徑分析,檔案定位和編譯執行的過程,大大提高了再次載入模組時的效率。
但在檔案定位過程中,還是一些細節需要主意,這主要包括副檔名的分析,目錄和包的處理
1.副檔名分析
require()在分析識別符號的過程中,會出現識別符號中不存在副檔名的情況。在這個情況下,nodejs會按.js,.node,.json的次序補充副檔名,依次嘗試。在嘗試的過程中需要呼叫fs模組同步阻塞式的判斷檔案是否存在。所以,如果是.node或.json檔案加上副檔名會加快一點速度。
2.目錄分析和包
在分析識別符號的過程中,require()通過分析副檔名之後,可能沒有查詢到對應的檔案,但是得到一個目錄,這是nodejs會將目錄當作一個包處理。
在這個過程中,首先,nodejs在當前目錄下查詢package.json,通過JSON.parse()解析出包描述物件,從中取出main屬性指定的檔名進行定位,如果檔名缺少副檔名,就將進入副檔名分析的步驟。如果main屬性指定的檔名錯誤,或者沒有package.json檔案,就將index作為預設檔名,然後依次查詢index.js,index.node,index.json。如果在目錄分析過程中沒有定位成功任何檔案,則自定義模組進入下一個模組路徑進行查詢,如果所以的模組路徑都查詢完畢還是沒有找到目標檔案,則會丟擲查詢異常。
四.模組編譯
在nodejs中,每一個檔案模組都是一個物件
執行和編譯是引入檔案模組的最後一個階段。定位到具體檔案後,nodejs會新建一個模組物件,然後根據路徑載入並編譯,對於不同的副檔名載入的方法有所不同。
1..js檔案。通過fs模組同步讀取檔案後編譯執行
2..node檔案。這是通過c/c++編寫的擴充套件檔案。通過dlopen()載入最後編譯生成的檔案
3..json檔案。通過fs模組同步讀取檔案後,用JSON.parse()解析返回結果
4.其他副檔名檔案。它們都被當作js檔案載入
注:《深入淺出nodejs》學習筆記