Circular Dependency

Circular Dependency

2022-12-15

在 JavaScript 開發中,Circular Dependency(循環依賴)是一個常見但也容易被忽視的問題。簡單來說,當模組 A 引用了模組 B,而模組 B 又反過來引用了模組 A,就形成了循環依賴。這種情況不只發生在兩個檔案之間,也可能是一連串的引用鏈:A -> B -> C -> A。

為什麼這是一個問題?

JavaScript 的模組系統(無論是 CommonJS 還是 ES Modules)在處理循環依賴時,通常不會直接報錯導致程式崩潰,但會導致未預期的行為。

最常見的情況是,當模組 A 正在執行並試圖引用模組 B 時,如果模組 B 因為循環依賴的關係還沒有完全初始化,那麼 A 拿到的模組 B 內容可能是不完整的,甚至是一個空物件。這會導致 undefined 錯誤,而且這類錯誤通常很難除錯,因為它們取決於模組的載入順序。

如何解決?

解決循環依賴的根本方法是重構程式碼。通常有以下幾種策略:

  1. 與其依賴,不如傳入:將依賴轉為參數傳入,而不是直接 import。
  2. 提取共用邏輯:將 A 和 B 共同需要的部份提取到第三個模組 C。讓 A 和 B 都依賴 C,而不是彼此依賴。

使用工具檢測

單靠人眼檢查很難發現複雜的循環依賴。我們可以利用工具來協助。這裡推薦一個輕量且快速的靜態分析工具:dpdm

dpdm 能夠解析 TypeScript 和 JavaScript 檔案,並找出其中的循環依賴。它的優點是不需要複雜的設定檔,直接透過 CLI 指令就能使用。

安裝方式:

pnpm install -D dpdm

使用方式非常簡單,直接指定你的入口檔案即可:

pnpm dlx dpdm ./src/index.ts
# 或者
pnpm dlx dpdm ./src/main.js

執行後,如果專案中有循環依賴,dpdm 會直接列出具體的檔案路徑和引用鏈,讓你一目了然。如果沒有問題,它會顯示 “No circular dependencies found”。

相比於整合在 Webpack 中的 plugin,dpdm 作為一個獨立的 CLI 工具,可以在 CI/CD 流程中更靈活地使用,確保程式碼在庫存入前就解決掉循環依賴的問題。