1. 程式人生 > 其它 >淺析nodejs的require函式分別載入自定義模組和npm開源庫的不同載入原理、NodeJS模組載入機制require和module的理解

淺析nodejs的require函式分別載入自定義模組和npm開源庫的不同載入原理、NodeJS模組載入機制require和module的理解

一、require 函式

1、require 函式是什麼?

  首先,直接說require函式的功能:用來載入目標js庫,並返回目標js庫公開的屬性成員函式/變數。

  我們在終端 node shell 輸入  this.require === require,可以看到為 true

  由此可得出結論:require是Node引擎上下文context的內建物件屬性,也就是全域性物件的require屬性,可呼叫或者使用this.require也行。

2、require 載入 node 內建模組

  我們都知道 require 可以用來載入 node 的內建模組。比如: require('fs') // 載入檔案系統模組

3、那麼非內建的模組,也想用require來載入,怎麼做呢?

  在當前目錄下,我們新建一個 test.js,內容如圖;然後,試著用require來載入看看。

  require函式能夠載入這個 test.js,不過不像內建模組一樣,需要通過給檔案路徑來定位到該 js 檔案,如:require('./test') 或 require('./test.js'),這裡我們看到列印的物件沒有任何屬性,require返回值為 :{}  // 沒有任何屬性

二、require 函式載入原理

由於NodeJS模組都遵循了CommonJS規範,根據CommonJS規範,JS庫的開發者如果需要開發某些函式對外部模組使用,需要使用module.exports或者exports

具體如下:module.exports.屬性名 = 函式引用        // 這裡將當前JS內的某個函式賦給 module.exports

或者:exports.屬性名 = 函式引用

  上面那個 test.js 相當於

const products = {data:[]}
function getData(){
  return products.data;
}
//預設情況,module.exports 這個物件沒有任何屬性,如下程式碼
module.exports = {}

  修改 test.js 檔案,繼續在終端跑 require('./test') 看,輸出仍舊為:{}

  為什麼呢?我們明明修改了匯出的內容啊?

  這裡先說一下結論:我們需要先退出當前node終端,重新進入終端,才可以匯入修改後的內容。這裡請思考一下為什麼需要重新開啟一個node REPL終端呢?

  可以看到重新開啟後,就可以獲取到值了。再檢視 require 發現上面多了很多東西。

三、怎麼載入npm registry上的庫?

  比如輸入require('lodash') 馬上發現錯誤了,“Cannot find module 'lodash'",這個錯誤經常容易見到(有時候拿到一個NodeJS專案忘記跑npm install了)

  通常我們需要使用命令安裝JS庫:npm install 目標JS庫名,再來使用它共享的功能。

  我們試試看,安裝完lodash庫之後,繼續在Node終端輸入require('lodash') 可以使用了,這裡不需要重啟(這又是為什麼呢?為什麼不需要重啟呢?)。

1、為什麼 node 終端能夠載入到 test.js,export內部函式之後又需要重啟,引入外部JS庫又不需要重開一個Node REPL終端?

  這裡需要講講require的另一個夥伴:module函式。它跟require函式一樣都掛載在上下文中,也是全域性物件的一個屬性,它的作用是管理整個專案的模組。

  進入node終端,列印 module,可以顯示模組載入的細節,這裡稍微留意一下children(裡面就有我們的 test.js 檔案)。

2、解答”Cannot find module"問題

  paths非空,我們使用require載入函式的時候,node引擎會從內建模組和paths對應的路徑去查詢模組,找不到才會丟擲類似異常:“Cannot find module 'lodash'"

  當我們跑了npm install 庫名,對應模組被下載到node_module目錄,載入的時候才能定位到庫,正常使用該庫功能

  在含有package.json的目錄中,執行npm install命令,可以一次性下載dependencies屬性宣告的全部依賴庫

3、解答是否需要重啟Node REPL?或者修改程式碼是否需要重啟正在的NodeJS程序的問題?

  繼續在終端輸入require('./test') ,然後輸入module, 再次輸出module物件,它的children已經多了一個Module物件(id對應到了test.js)

  當我們修改了 test.js,再次 require("./test") 的時候,node引擎發現module物件已經記錄載入過 test.js 了,不會重新進行載入。

  所以,這也就是雖然我們最新程式碼匯出了getData函式,可是我們載入到的仍舊是:{}  無任何函式匯出的原因。

4、解答為何npm install lodash之後能夠直接在node終端直接require就可以生效?

  因為node啟動,預設會查詢到當前目錄下的node_modules目錄(不管目錄存在不存在),當我們require一個不存在的js模組的時候,module物件找不到模組,它的children屬性並不會有任何變動。

  當安裝了 lodash 後,它就會在 node_modules 裡存在,當再次去查詢時就可以查到了。因此npm上的開源ks庫,我們只需要安裝了,就可以require載入。

5、總結:

  require和module互相協作產生的模組載入機制,是整個NodeJS開源文化的基石之一;而CommonJS就是一個脫離了框架的協議。這也在很多語言中反覆出現,像python/java的import包,CommonJS就像一個包協議,約定了庫的共享的標準格式,npm對標maven central/python libs。這套協議加上載入模式相關的介面模式,很值得借鑑。