前情提要
在做電商平台時,遇到一個問題:要同時支援多家金流(TapPay、ECPay、藍新),
PaymentService 是同一個 interface,但有三個不同的實作。
這是 DI 的經典場景,自然就想到引入 DI container 來管理。
調查一輪之後,才發現 Next.js 的編譯器對這件事有一個很重要的限制。
Next.js 預設用 SWC 編譯
SWC 是用 Rust 寫的 JavaScript/TypeScript 編譯器,Next.js 從 v12 開始改用它作為預設編譯器。
快很多,這是事實。但問題來了。
InversifyJS 和 tsyringe 為什麼不能用
InversifyJS 和 tsyringe 是目前最主流的兩個 TypeScript DI framework,
兩個都靠裝飾器(decorator)做依賴注入:
1 | () |
這個模式需要兩個東西:
emitDecoratorMetadata:TypeScript 編譯時把型別資訊保留下來reflect-metadata:在執行期讀取那些型別資訊
問題在於:SWC 對 emitDecoratorMetadata 的支援不完整。
SWC 的目標是快,不是完整複製 tsc 的行為。reflect-metadata 依賴的 metadata 在 SWC 編譯後不保證存在,跑起來會出錯或行為異常。
要硬用 InversifyJS,得把 Next.js 的編譯器換回 tsc,放棄 SWC 的效能優勢。代價太高。
awilix 為什麼可以
awilix 完全不用裝飾器,也不依賴 reflect-metadata。
它靠的是命名慣例:建構子參數的名稱對應 container 裡的 key。
1 | // container 裡這樣註冊 |
名稱對上,awilix 自動注入。不需要編譯器幫你保留任何型別資訊,SWC 完全相容。
三家比較
| InversifyJS | tsyringe | awilix | |
|---|---|---|---|
| 週下載量 | 1.5M | 600K | 400K |
| 裝飾器 | 需要 | 需要 | 不需要 |
| reflect-metadata | 需要 | 需要 | 不需要 |
| Next.js / SWC | ❌ | ❌ | ✅ |
| 學習曲線 | 高 | 中 | 低 |
下載量 InversifyJS 最高,但 Next.js 專案用不了。
Next.js 能用的只有 awilix。
業界趨勢
2026 的調查顯示,小型 Node.js 專案的趨勢反而是遠離 DI container,
回到手寫的 module-level singleton 或手動 dependency passing。
這不是說 DI container 不好,而是:
- 依賴圖簡單時,手寫 container 清楚又好讀
- 依賴圖複雜到手寫開始讓人痛了,再引入才是真正有感的投資
對我的情況,引入 awilix 的理由不只是技術問題,
還有早期建立團隊共識:早期引入就變成慣例,晚期引入沒人敢動。
小結
Next.js 用 SWC,SWC 不完整支援 reflect-metadata,所以 InversifyJS 和 tsyringe 不能用。
Next.js 專案要引入 DI container,awilix 是目前唯一合理的選擇。
(fin)