結論先講

同一個底層(Express.js + PM2 × 4),TypeScript 比 JavaScript 快、Sequelize 比 TypeORM 快、NestJS 的 DI 有可見但不致命的開銷。 Express-TS 在 50 VU 時 58 RPS 排 Node.js 生態第一,NestJS 39 RPS 第二,Express-JS 23 RPS 最後——JS 版不是更快,是更慢。


三者的配置

項目Express-TSExpress-JSNestJS
語言TypeScriptJavaScriptTypeScript
框架Express.js 4Express.js 4NestJS (Express adapter)
ORMSequelizeSequelizeTypeORM
ProcessPM2 cluster × 4PM2 cluster × 4PM2 cluster × 4
DB Pool202020
Hashbcrypt (native)bcrypt (native)bcrypt (native)

三者都用 native bcrypt,不是 bcryptjs。PM2 cluster mode 開 4 個 instance,等於 4 個 Node.js process。


Per-VU 數據對照

10 VU

框架AvgP95RPS
Express-TS71ms300ms24
NestJS86ms434ms23
Express-JS120ms223ms15

Express-TS 最快。NestJS 比 Express-TS 慢 15ms(DI + middleware overhead)。Express-JS 最慢——等等,JavaScript 不是應該比 TypeScript 快嗎?

50 VU

框架AvgP95RPS
Express-TS486ms1,593ms58
NestJS871ms2,507ms39
Express-JS1,510ms2,598ms23

差距更大了。Express-TS 58 RPS,Express-JS 只有 23 RPS——2.5 倍差距

100 VU

框架AvgP95RPS
Express-TS1,470ms3,858ms52
NestJS1,623ms5,182ms47
Express-JS3,444ms10,294ms23

Express-JS 的 P95 飆到 10 秒。Express-TS 和 NestJS 之間的差距在縮小(bcrypt 開始主導)。


為什麼 TypeScript 比 JavaScript 快

這違反直覺。TypeScript 需要編譯成 JavaScript 才能跑,多一步應該更慢才對?

關鍵在 V8 的 JIT 優化。

TypeScript 的 strict mode 給 V8 更多型別資訊。V8 在 JIT compile 時可以做更激進的優化——例如 inline caching、hidden class optimization。

// TypeScript — V8 知道 user 的結構,可以優化
interface User { id: number; email: string; }
const user: User = await User.findByPk(id);
 
// JavaScript — V8 要在 runtime 才能推斷型別
const user = await User.findByPk(id);

在長時間運行(壓測跑好幾分鐘),V8 有足夠的時間做 JIT 優化。TypeScript 的型別資訊讓 JIT 更早、更準確地做優化。

另外,TypeScript 的編譯過程會做一些靜態分析和 tree shaking,產出的 JavaScript 可能比手寫的 JavaScript 更「V8 friendly」。

但差距不應該 2.5 倍

15 RPS vs 58 RPS 的差距不可能只是 V8 JIT 造成的。更可能的原因是 Express-JS 版本的程式碼品質——可能有 N+1 查詢、多餘的 middleware、或次優的 Sequelize 配置。

這是跨框架比較的一個limitation:我們盡量讓 9 個框架的程式碼邏輯一致,但不同語言/框架的「最佳實踐」不同。Express-JS 版可能沒有被優化到和 Express-TS 版同一水平。


NestJS 的 DI overhead

NestJS 比 Express-TS 慢多少?

VUExpress-TSNestJS差距
1071ms86ms+15ms (+21%)
50486ms871ms+385ms (+79%)
1001,470ms1,623ms+153ms (+10%)

低 VU 時差距小(15ms),高 VU 時 bcrypt 蓋住差異(+10%)。50 VU 時差距最大(+79%),因為這是「框架 overhead 還可見但 bcrypt 還沒完全主導」的過渡區。

DI overhead 來自哪裡

  1. Dependency Injection container: NestJS 在每次請求時要 resolve dependencies。Express-TS 直接 import
  2. Decorator metadata: NestJS 用 TypeScript decorator,runtime 有 Reflect.getMetadata() 成本
  3. Middleware pipeline: NestJS 的 Guards → Interceptors → Pipes → Controller → Interceptors 比 Express 的 middleware chain 長
  4. TypeORM vs Sequelize: TypeORM 的 query builder 比 Sequelize 多一層抽象

結論:DI overhead 有但不致命

在真實應用中,NestJS 的結構化(DI、Module、Guard、Pipe)帶來的開發效率提升遠超 15ms 的延遲代價。壓測數據說的是「NestJS 比 Express 慢 15ms」,不是「不要用 NestJS」。


PM2 Cluster:4 個 instance 夠嗎

三個框架都用 PM2 cluster × 4。但 Docker 只給 2 核 CPU——4 個 instance 搶 2 核,有沒有浪費?

對 I/O-bound 操作:4 instance > 2 instance。 因為 Node.js 是 single-thread,一個 instance 在等 DB 時 CPU 閒著,另一個 instance 可以用這段時間。

對 CPU-bound 操作(bcrypt):4 instance ≈ 2 instance。 因為 bcrypt 佔 CPU,4 個 instance 搶 2 核不會比 2 個 instance 快多少。

實際數字:Express-TS 在 50 VU 時 58 RPS。如果只開 2 instance,大約 35-40 RPS。4 instance 確實有幫助,但不是線性提升。


Node.js 生態的壓測建議

  1. 用 native bcrypt(不是 bcryptjs: 效能差 30%,而且不阻塞 event loop
  2. TypeScript 優先: V8 JIT 優化 + 更好的程式碼品質
  3. PM2 cluster instance 數 = CPU 核數 × 2: 在我們的環境下就是 4
  4. NestJS 的 overhead 可接受: 除非你的 SLA 要求 P95 < 100ms,否則 NestJS 的結構化值得那 15ms
  5. ORM 選擇: Sequelize 在原始效能上比 TypeORM 快,但 TypeORM 的 TypeScript 整合更好。看團隊偏好

下一篇

Python 雙雄 CRUD:Django vs FastAPI — Django 排最後,FastAPI 排中間。但 FastAPI 在 10 VU 時是全場最快的(64ms)。GIL 是怎麼影響壓測結果的?uvicorn 和 gunicorn 有什麼差別?


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:Go CRUD 壓測:低延遲王者的天花板在哪裡 → 下一篇:Python 雙雄 CRUD:Django vs FastAPI 的 GIL 困境