1. 程式人生 > >Node.js的require()的工作原理

Node.js的require()的工作原理

.com wrap 文件 code 並且 -type 用戶 this parent

大多數人都知道Node.js中require()函數做什麽的,但是有多少人知道它的工作原理呢?我們每天使用它加載庫包和模塊,但是它的內部行為原理很神秘。

  我們追尋Node模塊系統的核心: module.js,這個文件包含一個令人驚訝的神奇功能,它負責加載 編譯和緩存每個用過的文件,讓我們揭開它的神秘面紗。

function Module(id, parent) {    this.id = id;    this.exports = {};    this.parent = parent; 
  // ... 

  在module.js中可以發現這個Module類型,扮演兩個主要角色:首先,它提供一個所有Node.js模塊從其文件被加載時構建一個實例的基礎功能,甚至在文件運行時持久,這就是為什麽我們能夠將一些屬性加入module.exports,並在需要時返回它們。

  module的第二個事情是處理Node模塊的加載機制,標準的require函數其實是基於module.require的抽象,後者只是一個對Module._load的簡單包裝,加載方法處理每個文件的實際加載。看看它的代碼大概如下:

Module._load = function(request, parent, isMain) { 
  // 1.在Module._cache中檢查模塊是否被緩存
  // 2.如果緩存中為空,創建一個新的模塊實例。
  // 3. 保存到緩存
  // 4. 使用指定的名稱調用module.load() 
  //    在讀取了文件內容後將調用module.compile() 
  // 5.如果加載和分析文件時有錯誤
  //    從緩存中刪除壞的模塊
  // 6. 返回 module.exports 
}; 

  Module._load負責加載新的模塊並且管理模塊緩存,緩存每個模塊能夠降低文件的讀取頻率,從而提高性能,共享模塊實例允許像單例模塊那樣跨應用保存狀態。

  如果一個模塊在緩存中不存在,Module._load將讀取文件創建一個新的,讀取文件內容成功後會調用module._compile

  如果你註意上面第六步,你會看到返回的是module.exports,這就是為什麽當你定義公共接口時,可以使用exports和module.exports,因為它們確實是Model._load和require返回的。

  下面看看module._compile:

Module.prototype._compile = function(content, filename) { 
  // 1. 創建調用模塊需要的require標準函數
  // 2.將其他幫助方法加入require. 
  // 3.包裝JS代碼到一個函數,這個函數提供我們的require
  //  模塊, 比如變量本地化到模塊的作用域
  // 4.返回這個函數
}; 

  這裏有魔術發生,首先,一個特殊的標準require函數將被創建,這就是我們熟悉的require()函數,當函數自己包裝了Module.require,它還包含一些很少人知道的幫助屬性和方法,如:

  • require():加載一個外部模塊
  • require.resolve(): 根據其絕對路徑解決模塊名稱
  • require.main: 主要的模塊
  • require.cache: 所有緩存模塊
  • require.extensions: 基於文件的擴展名可用於編譯的方法。

  一旦require準備就緒,整個源碼將被包裝進一個新的函數,這個函數有require module和exports和其他暴露變量作為參數,這創建了模塊的一個新函數作用域,這樣就不會汙染Node.js環境的其余部分。

(function (exports, require, module, __filename, __dirname) { 
  // YOUR CODE INJECTED HERE! 你的代碼在這裏
}); 

  最後,這個包裝了模塊的函數將運行,整個Module._compile方法同步執行,這樣原來對Module._load方法調用將會等待這個代碼運行,然後才會完成,返回module.exports給用戶。

 

Node.js的require()的工作原理