在 Node.js 中的模組有大三類型, 要如何區別他們, 以及它們是如何被運作的, 將是這篇的重點
Node.js 中模組的三大類型
另外, 三大模組形態還可以將他們區分為兩種型態(標示符)
非路徑形式的模組
- 核心模組
- 第三方模組
路徑形式的模組
- 自己撰寫的模組
路徑形式的模組
只有自己撰寫的模組被歸類在 路徑形式的模組 當中, 它的特性是 require 時必須添加路徑, 如:
1 | let foo = require('./foo.js') |
透過相對路徑的方式, 找到你自己撰寫的 JS 檔案, 並且引入使用
或者透過絕對路徑, 如:
1 | let foo = require('/foo.js') |
開頭直接為 /
代表的是該檔案所屬的硬碟根目錄, 在 windows 系統中, 如果檔案位在 C 槽下的話, 則 require('/foo.js')
代表載入 C:/foo.js
, 不過這種方法幾乎不會使用, 只要了解就可以了。
.js 的後綴名可以省略
非路徑形式的模組
核心模組 與 第三方模組 都是屬於這個分類, 共同點是他們都不需要加上路徑, 只需要單純用該模組的名字來引入就可以了, 如:
引入核心模組 http
1 | let http = require('http') |
引入第三方模組 art-template
1 | let tpl = require('art-template') |
核心模組
當引入自己撰寫的模組時, 我們都很清楚, 其實我們引入的就是自己寫好的 JS 檔案。那麼當我們像 let http = require('http')
引入 http 模組時, 它到底引入的是什麼東西呢?
一樣是 JS 檔案
沒錯, 它的本質一樣是 JS 檔案, 實際上它是真實存在的一支檔案, 由 Node.js 開發人員所撰寫, 並且被編譯成二進制文件中。在開發時我們只需要知道它的名字, 需要時引入就可以了。
Node.js 是開源的, 原始碼都可以在 Github 上找到, 我們可以來看看能不能找到 http.js
這隻檔案
第三方模組
凡是第三方模組, 都必須透過 npm 套件管理工具下載, 例如:
1 | npm install art-template |
這時候檔案所在資料夾下多了一個 node_modules 資料夾, 裡面也會有一個 art-template 資料夾, 當中包含了這個套件的相關檔案。
現在就可以 require('套件名')
的方式來載入使用
1 | let tpl = require('art-template') |
第三方模組 和 核心模組 在載入時, 都不需要路徑, 他們的載入方式是一樣的, 因此不可能有任何一個 第三方模組 的名字是和 Node.js 的核心模組相同的, 因為套件開發者將套件上傳到 npm 時, 若名稱與核心模組一樣, 是無法上傳的。
如何判斷是否為第三方模組
既然第三方模組
與核心模組
載入的方式都不需要路徑, 那當 Node.js 引擎是如何判斷這個 require 是哪種類型呢? 以下用 第三方模組 art-template 當作範例來說明 Node.js 判斷模組類型的流程
- Node.js 引擎讀取到
let tpl = require('art-template')
- 不具有路徑, 判斷它並非 自己撰寫的模組
- 核心模組並沒有一個名字叫做
art-template
的, 判斷它並非 核心模組 - 排除以上兩個可能, 確定它是一個 第三方模組
第三方模組查找規則
現在 Node.js 知道這個 art-template 是一個第三方模組了, 接著它是如何知道具體該去載入哪支 JS 檔呢?
- 首先 Node 會優先從當前檔案所處資料夾中, 尋找有沒有
node_modules
資料夾- 若沒有 node_modules 資料夾, 則執行 A. (↓)
- 尋找 node_modules 內, 有沒有
art-template
資料夾- 若沒有 art-template 資料夾, 則執行 A.
- 尋找 art-template 內, 有沒有
package.json
檔案- 若沒有 package.json, 預設會 require
art-template/index.js
。 若 index.js 也不存在, 執行 A.
- 若沒有 package.json, 預設會 require
- 尋找
package.json
內的main
屬性, 而它的值將會是最終被 require 的檔案- 若沒有 main 屬性, 預設會 require
art-template/index.js
。若 index.js 也不存在, 執行 A.
- 若沒有 main 屬性, 預設會 require
A. 進入上一級資料夾找 node_modules, 直到找到該檔案硬碟根目錄為止, 若有找到則重複執行上述流程。若沒找到, 則報錯誤 can not find module xxx
上述的流程概念有點類似 JavaScript 的原型鏈
和作用域
特性, 就是不斷地往外查找, 你可以以同樣的思路去理解。