Node.js:模塊查找,引用及緩存機(jī)制詳解
1. Node.js的模塊載入方式與機(jī)制
Node.js中模塊可以通過文件路徑或名字獲取模塊的引用。模塊的引用會(huì)映射到一個(gè)js文件路徑,除非它是一個(gè)Node內(nèi)置模塊。Node的內(nèi)置模塊公開了一些常用的API給開發(fā)者,并且它們在Node進(jìn)程開始的時(shí)候就預(yù)加載了。
其它的如通過NPM安裝的第三方模塊(third-party modules)或本地模塊(local modules),每個(gè)模塊都會(huì)暴露一個(gè)公開的API。以便開發(fā)者可以導(dǎo)入。如
var mod = require('module_name')
此句執(zhí)行后,Node內(nèi)部會(huì)載入內(nèi)置模塊或通過NPM安裝的模塊。require函數(shù)會(huì)返回一個(gè)對象,該對象公開的API可能是函數(shù),對象,或者屬性如函數(shù),數(shù)組,甚至任意類型的JS對象。
這里列下node模塊的載入及緩存機(jī)制
- 載入內(nèi)置模塊(A Core Module)
- 載入文件模塊(A File Module)
- 載入文件目錄模塊(A Folder Module)
- 載入node_modules里的模塊
- 自動(dòng)緩存已載入模塊
一、載入內(nèi)置模塊
Node的內(nèi)置模塊被編譯為二進(jìn)制形式,引用時(shí)直接使用名字而非文件路徑。當(dāng)?shù)谌降哪K和內(nèi)置模塊同名時(shí),內(nèi)置模塊將覆蓋第三方同名模塊。因此命名時(shí)需要注意不要和內(nèi)置模塊同名。如獲取一個(gè)http模塊
var http = require('http')
返回的http即是實(shí)現(xiàn)了HTTP功能Node的內(nèi)置模塊。
二、載入文件模塊
絕對路徑的
var myMod = require('/home/base/my_mod')
或相對路徑的
var myMod = require('./my_mod')
注意,這里忽略了擴(kuò)展名“.js”,以下是對等的
var myMod = require('./my_mod') var myMod = require('./my_mod.js')
三、載入文件目錄模塊
可以直接require一個(gè)目錄,假設(shè)有一個(gè)目錄名為folder,如
var myMod = require('./folder')
此 時(shí),Node將搜索整個(gè)folder目錄,Node會(huì)假設(shè)folder為一個(gè)包并試圖找到包定義文件package.json。如果folder 目錄里沒有包含package.json文件,Node會(huì)假設(shè)默認(rèn)主文件為index.js,即會(huì)加載index.js。如果index.js也不存在, 那么加載將失敗。
假如目錄結(jié)構(gòu)如下
package.json定義如下
{ "name": "pack", "main": "modA.js" }
此時(shí) require('./folder') 將返回模塊modA.js。如果package.json不存在,那么將返回模塊index.js。如果index.js也不存在,那么將發(fā)生載入異常。
四、載入node_modules里的模塊
如果模塊名不是路徑,也不是內(nèi)置模塊,Node將試圖去當(dāng)前目錄的node_modules文件夾里搜索。如果當(dāng)前目錄的node_modules里沒有找到,Node會(huì)從父目錄的node_modules里搜索,這樣遞歸下去直到根目錄。
不必?fù)?dān)心,npm命令可讓我們很方便的去安裝,卸載,更新node_modules目錄。
五、自動(dòng)緩存已載入模塊
對于已加載的模塊Node會(huì)緩存下來,而不必每次都重新搜索。下面是一個(gè)示例
modA.js
console.log('模塊modA開始加載...') exports = function() { console.log('Hi') } console.log('模塊modA加載完畢')
init.js
var mod1 = require('./modA') var mod2 = require('./modA') console.log(mod1 === mod2)
命令行執(zhí)行:
node init.js
輸入如下
可以看到雖然require了兩次,但modA.js仍然只執(zhí)行了一次。mod1和mod2是相同的,即兩個(gè)引用都指向了同一個(gè)模塊對象。
2. nodejs 模塊查找
nodejs在加載外部自定義模塊時(shí)對模塊有查找順序,找到后還會(huì)進(jìn)行緩存。
查找順序:
1. 相對路徑,比如提供./ 或者../這種以'./'和‘..’開始的路徑,簡單的,就是相對當(dāng)前位置的路徑。
2.絕對路徑,這時(shí)候?qū)匆韵马樞虿檎遥?/p>
在 進(jìn)入路徑查找之前有必要描述一下module path這個(gè)Node.js中的概念。對于每一個(gè)被加載的文件模塊,創(chuàng)建這個(gè)模塊對象的時(shí)候,這個(gè)模塊便會(huì)有一個(gè)paths屬性,其值根據(jù)當(dāng)前文件的路徑 計(jì)算得到。我們創(chuàng)建modulepath.js這樣一個(gè)文件,其內(nèi)容為:
console.log(module.paths);
我們將其放到任意一個(gè)目錄中執(zhí)行node modulepath.js命令,將得到以下的輸出結(jié)果。
[ '/home/ikeepstudying/research/node_modules', '/home/ikeepstudying/node_modules', '/home/node_modules', '/node_modules' ]
Windows下:
[ 'c:\\nodejs\\node_modules', 'c:\\node_modules' ]
然后是['.']
然后是:
windows下%NODE_PATH%,%USERPROFILE%/.node_modules, %USERPROFILE%/.node_libraries
非windows下$NODE_PATH, $HOME/.node_modules, $HOME/.node_libraries
[NODE_PATH,HOME/.node_modules,HOME/.node_libraries,execPath/../../lib/node]
然后是node.exe目錄的../../lib/node,所以這個(gè)具體取決于node二進(jìn)制文件放哪里.
簡而言之,如果require絕對路徑的文件,查找時(shí)不會(huì)去遍歷每一個(gè)node_modules目錄,其速度最快。其余流程如下:
- 從module path數(shù)組中取出第一個(gè)目錄作為查找基準(zhǔn)。
- 直接從目錄中查找該文件,如果存在,則結(jié)束查找。如果不存在,則進(jìn)行下一條查找。
- 嘗試添加.js、.json、.node后綴后查找,如果存在文件,則結(jié)束查找。如果不存在,則進(jìn)行下一條。
- 嘗試將require的參數(shù)作為一個(gè)包來進(jìn)行查找,讀取目錄下的package.json文件,取得main參數(shù)指定的文件。
- 嘗試查找該文件,如果存在,則結(jié)束查找。如果不存在,則進(jìn)行第3條查找。
- 如果繼續(xù)失敗,則取出module path數(shù)組中的下一個(gè)目錄作為基準(zhǔn)查找,循環(huán)第1至5個(gè)步驟。
- 如果繼續(xù)失敗,循環(huán)第1至6個(gè)步驟,直到module path中的最后一個(gè)值。
- 如果仍然失敗,則拋出異常。
整個(gè)查找過程十分類似原型鏈的查找和作用域的查找。所幸Node.js對路徑查找實(shí)現(xiàn)了緩存機(jī)制,否則由于每次判斷路徑都是同步阻塞式進(jìn)行,會(huì)導(dǎo)致嚴(yán)重的性能消耗。
一旦加載成功就以模塊的路徑進(jìn)行緩存,這里有一個(gè)陷阱。
就是如果父目錄包含X模塊,且存在引用X模塊的代碼。而子目錄也是相同的情況。那么父目錄和子目錄下實(shí)際引用到的分別是自己目錄下的那個(gè)X模塊,而不是之前那個(gè)的復(fù)用。也就是要注意他緩存是匹配全路徑的。
Nodejs:模塊查找,引用及緩存機(jī)制
3. 利用nodejs模塊緩存機(jī)制創(chuàng)建“全局變量”
在《深入淺出nodejs》有這樣一段(有部分增減):
nodejs引入模塊分四個(gè)步驟 路徑分析 文件定位 編譯執(zhí)行 加入內(nèi)存 核心模塊部分在node源代碼的編譯過程中就編譯成了二級(jí)制文件,在node啟動(dòng)時(shí)就直接加載如內(nèi)存,所以這部分模塊引入時(shí),前三步省略,直接加入。 nodejs的模塊加載和瀏覽器js加載一樣都有緩存機(jī)制,不同的是,瀏覽器僅僅緩存文件,而nodejs緩存的是編譯和執(zhí)行后的對象(緩存內(nèi)存)。 基于以上三點(diǎn):我們可以編寫一個(gè)模塊,用來記錄長期存在的變量。例如:我可以編寫一個(gè)記錄接口訪問數(shù)的模塊:
var count = {}; // 因模塊是封閉的,這里實(shí)際上借用了js閉包的概念 exports.count = function(name){ if(count[name]){ count[name]++; }else{ count[name] = 1; } console.log(name + '被訪問了' + count[name] + '次。'); };
我們在路由里這樣引用:
var count = require('count'); export.index = function(req, res){ count('index'); };
4. nodejs清除require緩存 delete require.cache
開發(fā)nodejs應(yīng)用時(shí)會(huì)面臨一個(gè)麻煩的事情,就是修改了配置數(shù)據(jù)之后,必須重啟服務(wù)器才能看到修改后的結(jié)果。
于是問題來了,挖掘機(jī)哪家強(qiáng)?噢,no! no! no!
怎么做到修改文件之后,自動(dòng)重啟服務(wù)器。
server.js中的片段:
var port = process.env.port || 1337;app.listen(port); console.log("server start in " + port); exports.app = app;
假定我們現(xiàn)在是這樣的:
app.js的片段:
var app = require('./server.js');
如果我們在server.js中啟動(dòng)了服務(wù)器,我們停止服務(wù)器可以在app.js中調(diào)用
app.app.close()
但是當(dāng)我們重新引入server.js
app = require('./server.js')
的時(shí)候會(huì)發(fā)現(xiàn)并不是用的最新的server.js文件,原因是require的緩存機(jī)制,在第一次調(diào)用require('./server.js')的時(shí)候緩存下來了。
這個(gè)時(shí)候怎么辦?
下面的代碼解決了這個(gè)問題:
delete require.cache[require.resolve('./server.js')]; app = require('./server.js');
Node.js的模塊查找、引用和緩存機(jī)制是其獨(dú)特的特性之一,它們?yōu)殚_發(fā)人員提供了更好的性能和可維護(hù)性。了解這些機(jī)制的工作原理和使用方法,可以幫助開發(fā)人員更好地利用Node.js的優(yōu)勢,提高應(yīng)用程序的性能和可維護(hù)性。
到此這篇關(guān)于Node.js:模塊查找,引用及緩存機(jī)制詳解的文章就介紹到這了,更多相關(guān)Node.js模塊查找,引用及緩存機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
nodejs+express最簡易的連接數(shù)據(jù)庫的方法
這篇文章主要介紹了nodejs+express 最簡易的連接數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12把Node.js程序加入服務(wù)實(shí)現(xiàn)隨機(jī)啟動(dòng)
這篇文章主要介紹了把Node.js程序加入服務(wù)實(shí)現(xiàn)隨機(jī)啟動(dòng),本文使用qckwinsvc實(shí)現(xiàn)這個(gè)需求,講解了qckwinsvc的安裝和使用,需要的朋友可以參考下2015-06-06如何刪除所有node_modules和package-lock配置文件
這篇文章主要介紹了如何刪除所有node_modules和package-lock配置文件問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02