在 Node.js 中,若有載入多支 JS 檔需求時,並不能像以往前端開發一樣,用 <script> 標籤引入多支檔案,且它也沒有 HTML 檔案來支持你這麼做。Node.js 須要使用 require 的方式來載入其他的 JS 檔案,這也是模組系統的主要使用方法。
什麼是模組?
在 Node.js 中的模組(Module),指的就是單一支一支的 JS 檔案,透過互相引入的方式來達成繫節關係。
簡單範例
資料夾結構:
 nodejs-module
  ∟ A.js
  ∟ B.js
| 1 | // A.js | 
| 1 | // B.js | 
執行 node A.js
| 1 | A.js 開始執行了! | 
也就是說,當你執行某支檔案時,程式碼執行到 require 字眼時,Node.js 就會執行被 require 那支檔案內的程式碼。
在 Node.js 中沒有全域作用域,只有模組作用域
資料夾結構:
 nodejs-module
  ∟ A.js
  ∟ B.js
| 1 | // A.js | 
| 1 | // B.js | 
在這個例子當中,若是以以往 <script> 載入的概念來看是像這樣的
| 1 | <script src="A.js"></script> | 
全域的 foo 變數會被後來載入的 B.js 內的 foo 覆蓋,預期應 foo 應該會是 B。但是在 Node.js 中是這樣嗎?
試著來輸出結果看看
| 1 | A.js 開始執行 | 
延續上面的例子,如果我們在 A.js 中定義一個函數,並試著在 B.js 呼叫這個函數,能夠成功嗎?
| 1 | // A.js | 
| 1 | // B.js | 
輸出結果
| 1 | console.log(add(10, 5)) | 
由上面的例子我們可以理解所謂模組作用域其實就是
檔案作用域,只要不是寫在自己本身的變數或函式,即使我載入了你,我還是不能取用你的東西的,載入就是單純執行對方內部的程式碼而已。
注意事項
- require 時副檔名可以省略 - 1 - require('./B.js') // === require('./B') 
- 載入自己寫的模組(JS檔),相對路徑不得省略 - 1 - require('B.js') // Cannot find module 'B' 
模組間的通信
上面的例子或許讓你感到很困惑,既然載入了它,不就是為了要取用它的部分成員嗎?如果只是執行內部的程式碼,而不能取用成員,為何還要載入它呢?
是的,Node.js 中預設模組是封閉了,你可以載入我,但你取用不到我任何的成員,內部無法取用外部; 外部也無法取用內部。
但有時候載入模組的目的不只是單純為了執行裡面的程式碼,更重要的是取用裡面的成員阿!
那,該怎麼做?
導出 (exports)
Node 中的 require 方法有兩個作用
- 載入模組並執行裡面的程式碼 (也就是上面的例子)
- 拿到被加載模組所導出的接口物件- 在每個模組中都提供一個物件 exports,我們可以將它導出,並讓其他模組接收,而它預設是一個空的物件
 
- 在每個模組中都提供一個物件 
資料夾結構:
 nodejs-module
  ∟ A.js
  ∟ B.js
| 1 | // A.js | 
| 1 | // B.js | 
執行 node A.js 結果
| 1 | B.js 被加載執行了 | 
A.js利用一個變數 require 了 B.js 的接口物件。結果發生了兩件事
- 執行了 B.js 檔案內的程式碼
result變數接收了 B.js 的接口物件,由於 B.js 沒有導出任何成員,因此 A.js 接到的是空物件。
你可以試著在任一模組裡,印出 exports 物件,證明它預設就是一個空物件
| 1 | // B.js | 
執行 node B,果然得到一個空物件
| 1 | {} | 
導出成員
了解上面的原理後,接著我們可以開始導出成員了,只需要將想被導出的成員放入 exports 物件中,接著讓須要取用的模組 require 即可
| 1 | // B.js | 
| 1 | // A.js | 
輸出 node A.js 結果
| 1 | B |