[摘抄] 4.require命令

4.require命令

1. 基本用法

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"。緩存

4.2 加載規則

require命令用於加載文件,後綴名默認爲.jsbash

var foo = require('foo');
//  等同於
var foo = require('foo.js');

根據參數的不一樣格式,require命令去不一樣路徑尋找模塊文件。函數

(1)若是參數字符串以「/」開頭,則表示加載的是一個位於絕對路徑的模塊文件。好比,require('/home/marco/foo.js')將加載/home/marco/foo.jsui

(2)若是參數字符串以「./」開頭,則表示加載的是一個位於相對路徑(跟當前執行腳本的位置相比)的模塊文件。好比,require('./circle')將加載當前腳本同一目錄的circle.js設計

(3)若是參數字符串不以「./「或」/「開頭,則表示加載的是一個默認提供的核心模塊(位於Node的系統安裝目錄中),或者一個位於各級node_modules目錄的已安裝模塊(全局安裝或局部安裝)。code

舉例來講,腳本/home/user/projects/foo.js執行了require('bar.js')命令,Node會依次搜索如下文件。

  • /usr/local/lib/node/bar.js
  • /home/user/projects/node_modules/bar.js
  • /home/user/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

這樣設計的目的是,使得不一樣的模塊能夠將所依賴的模塊本地化。

(4)若是參數字符串不以「./「或」/「開頭,並且是一個路徑,好比require('example-module/path/to/file'),則將先找到example-module的位置,而後再以它爲參數,找到後續路徑。

(5)若是指定的模塊文件沒有發現,Node會嘗試爲文件名添加.js.json.node後,再去搜索。.js件會以文本格式的JavaScript腳本文件解析,.json文件會以JSON格式的文本文件解析,.node文件會以編譯後的二進制文件解析。

(6)若是想獲得require命令加載的確切文件名,使用require.resolve()方法。

4.3 目錄的加載機制

一般,咱們會把相關的文件會放在一個目錄裏面,便於組織。這時,最好爲該目錄設置一個入口文件,讓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文件。

4.4 模塊的緩存

第一次加載某個模塊時,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命令仍是會從新加載該模塊。

4.5 環境變量NODE_PATH

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目錄機制。

4.6 模塊的循環加載

若是發生模塊的循環加載,即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語句都不會執行了。

4.7 require.main

require方法有一個main屬性,能夠用來判斷模塊是直接執行,仍是被調用執行。

直接執行的時候(node module.js),require.main屬性指向模塊自己。

require.main === module
// true

調用執行的時候(經過require加載該腳本執行),上面的表達式返回false。

相關文章
相關標籤/搜索