Node.js 是怎么找到模塊的?你知道嗎?
大家好,我是前端西瓜哥,今天我們來看看 Node.js 模塊查找的原理。
模塊種類
模塊有三種來源。
- 核心模塊:Node.js 內(nèi)置的包。比如 http、fs、path。
- 自定義模塊:NPM 包。比如 axios、express,位于 node_modules 目錄下的同名目錄,并通過 package.json 的 main 字段指定入口文件。
- 文件模塊:項(xiàng)目自己的模塊文件,使用路徑的寫法。包括相對(duì)路徑(比如 "./utils")和絕對(duì)路徑(比如 "/Users/xigua/project/utils")。
需要注意的是,"a/b" 這種不屬于路徑寫法,它屬于前兩種,比如 "fs/promises"、"@babel/core"。
這里給一個(gè)例子:
模塊查找
我們使用 require() 方法,傳入一個(gè)字符串標(biāo)識(shí)符,模塊查找的旅途就開始了。
核心模塊
首先分析標(biāo)識(shí)符的風(fēng)格,如果是不是路徑的寫法,我們會(huì)先找 Node.js 內(nèi)置的包有沒有匹配的,如果匹配,就導(dǎo)入對(duì)應(yīng)模塊,比如 require('http') 就能拿到一個(gè) http 對(duì)象,可用于創(chuàng)建 web 服務(wù)等功能。
NPM 包
如果不匹配,會(huì)在當(dāng)前文件的目錄下,找 node_modules 目錄,看里面有沒有對(duì)應(yīng)的包。如果找不到,就繼續(xù)往父目錄找,直到根目錄。如果找不到,會(huì)報(bào) Cannot find module '包名' 的錯(cuò)誤。
文件模塊
包通常是一個(gè)文件夾,里面會(huì)有 package.json 文件,Node.js 會(huì)提取其中 main 字段對(duì)應(yīng)的文件作為模塊文件。如果沒有,就依次查找該目錄下的 index.js、index.json、index.node 文件。
需要查找的目錄可以通過 module.paths 變量得到。
如果你熟悉 JavaScript 的原型鏈,你會(huì)發(fā)現(xiàn)它們非常相似,可以做類比以加深理解。
如果標(biāo)識(shí)符是路徑,會(huì)通過計(jì)算得到一個(gè)絕對(duì)路徑,然后找到的是個(gè)目錄,同上面找 npm 包的邏輯。
要是找不到,就加上后綴再找。后綴按順序添加為:.js 、.json、.node,找到就立即返回。若一個(gè)文件沒有后綴但被匹配到了,它會(huì)被當(dāng)作 js 文件。
上面沒說緩存的情況,其實(shí)我們會(huì)對(duì)模塊做緩存,下面詳細(xì)說明一下。
模塊緩存
每當(dāng)加載一個(gè)模塊后,這個(gè)模塊就會(huì)被緩存起來。
你可以在隨意一個(gè)文件中輸入得到緩存的內(nèi)容,是一個(gè)哈希表,key 為模塊的絕對(duì)路徑,確保緩存命中,value 則是模塊對(duì)象。
也能用 require.cache 變量拿到,它和 Module._cache 指向同一個(gè)對(duì)象。
Node.js 內(nèi)置的模塊也需要緩存,但它不會(huì)記錄到 Module._cache 中,而是保存在 Module.
下面是一個(gè)例子,index.js 導(dǎo)入了 a.js,a.js 下引入了 lodash.get 包,模塊緩存結(jié)果為:
因?yàn)榫彺娴拇嬖?,所?nbsp;一個(gè)模塊文件只會(huì)被執(zhí)行一次,然后將 module.exports 緩存下來。
之后被多次導(dǎo)入,不會(huì)再執(zhí)行這個(gè)模塊文件,而是直接取出對(duì)應(yīng)的 module.exports。
總結(jié)
畫了個(gè)流程圖,丟掉了一些細(xì)節(jié)(路徑定位到目錄后的邏輯)。