本文同步自我的個人博客:http://www.52cik.com/2015/12/11/learn-node-modules-path.html
用了這么久的 require,但卻沒有系統的學習過 node 的模塊系統,今天就翻官方文檔系統的學習下。
循環引用
node 對模塊循環引用做了相應的處理,防止無盡的循環。
官方例子:
a.js:
console.log('a starting');
exports.done = false;
var b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js:
console.log('b starting');
exports.done = false;
var a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js:
console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
執行結果:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
在結果中可以看到,b 里引用 a 的時候結果是 a.done = false,這就算文檔里說的 unfinished copy,這樣就避免了程序的死循環。
核心模塊
其實就是被編譯到 node 內部的一些模塊,在源碼的 lib 目錄下可以找到。例如 require('http') 的時候不論是否有其他同名模塊,都直接返回 node 的核心模塊,也就算最高優先級。
這些模塊就是 node 官網文檔目錄列表里的那些模塊。
文件模塊
這個花樣就多了,慢慢說吧。
模塊后綴
node 默認支持 .js .json .node 后綴的模塊,也就是說 require 的時候不需要寫后綴名。例如 require('./a'),node 會先嘗試當前目錄下的 a.js 文件,如果有,就載入否則繼續嘗試 a.json, a.node 這些文件,如果都不存在,就會拋出模塊未找到的錯誤。
當然這些模塊后綴都是可以自己定義拓展的,比如 ejs 就拓展了個 .ejs 的后綴,支持直接 require 就得到了編譯好的模塊引擎了。
模塊路徑
以 '/', './' 或 '../' 開頭的,都是指定路徑加載。
模塊以 '/' 為前綴,則表示絕對路徑。例如,require('/home/marco/foo.js') 加載的是 /home/marco/foo.js 這個文件。
模塊以 './' 為前綴,則路徑是相對於當前文件的。也就是說,circle.js 必須和 foo.js 在同一目錄下,require('./circle') 才能找到。
其實還一個特殊路徑的模塊加載,就是 node_modules 文件夾,這個要單獨說了。
從 node_modules 目錄中加載
如果模塊不是以 '/', './' 或 '../' 開頭的,那么 node 會從當前模塊的父目錄開始,嘗試在它的 node_modules 文件夾里加載相應模塊。如果沒有找到,那么就再向上移動到父目錄,直到到達頂層目錄位置。
假如 /home/ry/projects/foo.js 調用了 require('bar.js') 那么node查找的位置依次為:
- /home/ry/projects/node_modules/bar.js
- /home/ry/node_modules/bar.js
- /home/node_modules/bar.js
- /node_modules/bar.js
如果都找不到,會去全局模塊路徑找,還找不到就會拋出模塊未找到的錯誤。
目錄模塊
當一個模塊功能多代碼多的時候,不合適寫到一個文件里,那這個時候就可以用目錄當作模塊使用了。有兩種方法可以讓一個目錄作為 require 方式引入。官網說三種,其實吧 .js .node 看做兩種了。
index.js 入口
例如在 some-library 目錄里有 index.js 或者 index.node 文件,那么 require('./some-library') 就將嘗試加載下面的文件:
- ./some-library/index.js
- ./some-library/index.node
非常簡單方便。
package.json
還有一種方式可以指定入口文件,就是通過 package.json,下面是一個 package.json 文件的示例。
{
"name" : "some-library",
"main" : "./src/some-library.js"
}
那么 require('./some-library') 就將會去加載 ./some-library/src/some-library.js。
全局模塊
當以上方法找不到模塊時,node 會去全局配置下尋找全局模塊:
- $HOME/.node_modules
- $HOME/.node_libraries
- $PREFIX/lib/node
以及
- /usr/lib/node_modules
或者
- /usr/local/lib/node_modules
這些都沒測試,我只知道我本地的全局模塊路徑是 /usr/local/lib/node_modules,是我本機 npm 安裝的全局模塊路徑。
官網里還有一些奇葩的全局目錄,我都沒見過,算了不研究了。
