Node適用CommonJS模塊規範,內置的require
命令用於加載模塊文件。javascript
require
命令的基本功能是,讀入並執行一個JavaScript文件,而後返回該模塊的exports
對象。 若是沒有發現指定模塊,就會報錯。java
var invisible = function(){ console.log('invisible'); } exports.message = 'hi'; exports.say = function (){ console.log(message); }
運行下面的命令,能夠輸出exports
對象。node
var example = require('./example.js'); example{ message:'hi', say:[Function] }
若是模塊輸入的是一個函數,那就不能定義在exports對象上面,而要定義在module.exports
變量上面。json
module.exports = function (){ console.log('hello world') } require('./example.js')()
上面代碼中,require命令調用自身,等因而執行module.exports
,所以會輸出"hello world"。緩存
require
命令用於加載文件,後綴名默認爲.js
。bash
var foo = require('foo'); // 等同於 var foo = require('foo.js');
根據參數的不一樣格式,require
命令去不一樣路徑尋找模塊文件。函數
(1)若是參數字符串以「/」開頭,則表示加載的是一個位於絕對路徑的模塊文件。好比,require('/home/marco/foo.js')
將加載/home/marco/foo.js
。ui
(2)若是參數字符串以「./」開頭,則表示加載的是一個位於相對路徑(跟當前執行腳本的位置相比)的模塊文件。好比,require('./circle')
將加載當前腳本同一目錄的circle.js
。設計
(3)若是參數字符串不以「./「或」/「開頭,則表示加載的是一個默認提供的核心模塊(位於Node的系統安裝目錄中),或者一個位於各級node_modules目錄的已安裝模塊(全局安裝或局部安裝)。code
舉例來講,腳本/home/user/projects/foo.js
執行了require('bar.js')
命令,Node會依次搜索如下文件。
這樣設計的目的是,使得不一樣的模塊能夠將所依賴的模塊本地化。
(4)若是參數字符串不以「./「或」/「開頭,並且是一個路徑,好比require('example-module/path/to/file')
,則將先找到example-module
的位置,而後再以它爲參數,找到後續路徑。
(5)若是指定的模塊文件沒有發現,Node會嘗試爲文件名添加.js
、.json
、.node
後,再去搜索。.js
件會以文本格式的JavaScript腳本文件解析,.json
文件會以JSON格式的文本文件解析,.node
文件會以編譯後的二進制文件解析。
(6)若是想獲得require
命令加載的確切文件名,使用require.resolve()
方法。
一般,咱們會把相關的文件會放在一個目錄裏面,便於組織。這時,最好爲該目錄設置一個入口文件,讓require
方法能夠經過這個入口文件,加載整個目錄。
在目錄中放置一個package.json
文件,而且將入口文件寫入main
字段。下面是一個例子。
// package.json { "name" : "some-library", "main" : "./lib/some-library.js" }
require
發現參數字符串指向一個目錄之後,會自動查看該目錄的package.json
文件,而後加載main
字段指定的入口文件。若是package.json
文件沒有main
字段,或者根本就沒有package.json
文件,則會加載該目錄下的index.js
文件或index.node
文件。
第一次加載某個模塊時,Node會緩存該模塊。之後再加載該模塊時就直接從緩存取出該模塊的module.exports
屬性。
require('./example.js'); require('./example.js').mesage = 'hello'; require('./example.js').message; // "hello"
require
命令,加載同一個模塊。message
屬性。message
屬性依然存在,這就證實require
命令並無被從新加載,而是輸出了緩存。若是想要屢次執行某個模塊,可讓該模塊輸出一個函數,而後每次require
這個模塊的時候,從新執行一下輸出函數。
全部緩存的模塊保存在require.cache
之中,若是想刪除模塊的緩存,能夠像下面這樣寫。
// 刪除指定的模塊緩存 delete require.cache[moduleName]; // 刪除全部模塊的緩存 Objcet.keys(require.cache).forEach(function(key){ delete require.cache[key]; })
注意,緩存是根據絕對路徑識別模塊的,若是同的模塊名,可是保存在不一樣的路徑,require
命令仍是會從新加載該模塊。
Node執行一個腳本時,會先查看環境變量NODE_PATH
。他是一組以冒號分隔的絕對路徑。在其餘位置找不到指定模塊時,Node會去這些路徑查找。
能夠將NODE_PATH添加到.bashrc
。
export NODE_PATH="/usr/local/lib/node"
因此,若是遇到複雜的相對路徑,好比下面這樣
var myModule = require('../../../../lib/myModule');
有兩種解決方法,
node_modules
目錄NODE_PATH
環境變量package.json
文件能夠採用下面的寫法
{ "name": "node_path", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "NODE_PATH=lib node index.js" }, "author": "", "license": "ISC" }
NODE_PATH
是歷史遺留下來的一個路徑解決方案,一般不該該使用,而應該使用node_modules
目錄機制。
若是發生模塊的循環加載,即A加載B,B又加載A,則B將加載A的不完整版本。
// a.js // 【2】.文件a在exports中建立變量並賦值爲`a1` exports.x = 'a1'; // 【3】.導入文件b,文件b開始執行。 console.log('a.js ', require('./b.js').x); //b2 // 【7】.b文件執行完畢,a文件繼續往下執行,賦值,require.cache中a文件的x值變爲a2; exports.x = 'a2'; // b.js // 【4】.執行文件b,建立變量並賦值`b1` exports.x = 'b1'; // 【5】.導入文件a,文件a已經被執行過,因此在[require.cache]中是有a文件的緩存,而且exports.x = a1,下面則不會再執行a文件而是從緩存中獲得x值,爲a1; console.log('b.js ', require('./a.js').x); //a1 // 【6】.賦值,執行完畢 exports.x = 'b2'; // main.js // 【1】.開始讀取文件a console.log('main.js ', require('./a.js').x); //a2 // 【8】.a文件讀取完畢,往下執行讀取b文件,b文件在a文件的執行過程當中已經讀取,則拿出緩存直接打印 console.log('main.js ', require('./b.js').x); //b2
上面代碼是三個JavaScript文件。其中,a.js加載了b.js,而b.js又加載a.js。這時,Node返回a.js的不完整版本,因此執行結果以下。
$ node main.js //開始執行 b.js a1 a.js b2 main.js a2 main.js b2
修改main.js,再次加載a.js和b.js。
// main.js console.log('main.js ', require('./a.js').x); console.log('main.js ', require('./b.js').x); console.log('main.js ', require('./a.js').x); console.log('main.js ', require('./b.js').x);
執行上面代碼,結果以下。
$ node main.js b.js a1 a.js b2 main.js a2 main.js b2 main.js a2 main.js b2
上面代碼中,第二次加載a.js和b.js時,會直接從緩存讀取exports屬性,因此a.js和b.js內部的console.log語句都不會執行了。
require
方法有一個main
屬性,能夠用來判斷模塊是直接執行,仍是被調用執行。
直接執行的時候(node module.js
),require.main
屬性指向模塊自己。
require.main === module // true
調用執行的時候(經過require
加載該腳本執行),上面的表達式返回false。