JavaScript Runtime 是什麼?看懂「JS 引擎 + host API」,貫通 Node / Deno / Bun / Edge

Published on Updated on Written by

我們每天寫 JS / TS,它到底跑在什麼上面?答案是某個 runtime——而 runtime 就是一顆 JS 引擎(V8、JavaScriptCore…)+ 一組 host API(決定 JS 看得到檔案、網路、process,還是只看得到 DOM)。一旦握住「引擎 + host API」這個骨架,一票本來要死背的問題會從同一個模型自然推出來——為什麼 Cloudflare Workers 沒有 fs、為什麼 React SSR 有兩個串流 API、Node / Deno / Bun / Edge 到底差在哪、哪些 code 換個環境就跑不動。

為什麼值得學

  • 你早就在用 Node.js、只是沒意識到。 npm installnext devvitetsceslintprettier——前端工具鏈幾乎全都是「跑在 Node 上的程式」。即使你不寫 server,你的 dev loop 已經是 Node。
  • SSR、邊緣部署、build tooling 都站在 runtime 上。 講「React SSR」其實是「React 在某個 JS runtime 上 render HTML」;講「邊緣部署」其實是「我的 code 跑在 Edge runtime 上」。runtime 是這些題的共同底層——略過這層,上層的選型討論會像在猜謎。
  • Web 標準正在跨 runtime 收斂——這兩年才真正成立。 WinterTC(Ecma TC55,前身為 W3C 的 WinterCG)正在把 fetch、Streams、Web Crypto、AbortController…這些原本是「瀏覽器專屬」的 API,訂成「所有 server-side runtime 都該有的最小共同集」,參與者包含 Node、Deno、Cloudflare、Vercel 等。寫越多 Web 標準 API,你的 code 越能跨 Node / Deno / Bun / Edge 跑。
  • AI agent 時代,runtime 是 agent 跑工具的家。 Claude Code 自己是 Node 應用、MCP server 多半是 Node、agent 在你本機跑的 JS / TS 腳本也都靠這層。寫工具給 agent 用、或讀懂 agent 在做什麼,避不掉 runtime 這層認識。

定位與脈絡

一句話:什麼是 JavaScript runtime

JS 引擎 + 一組 host API。 引擎負責把 JS 文字 parse、編譯、執行;host API 是引擎之外、host 額外給 JS 用的東西。差別不在引擎——幾乎全在 host API

Runtime引擎Host API 給你什麼
瀏覽器V8 / JSC / SpiderMonkeyDOM、windowdocument、Web API(fetch、Streams…)
Node.jsV8fsnethttpchild_processprocessBuffer
DenoV8Web API 優先(fetch、Streams…)+ Deno.*
BunJavaScriptCoreWeb API + Bun.* + 大部分 Node API(相容層)
Edge runtimeV8 isolatesWeb API only——沒有 fs、沒有 child_process

連你天天用的瀏覽器其實就是一種 JS runtime。所以「不在瀏覽器跑 JS」的真正意思是:換一組 host API——換成看得到檔案系統、網路 socket、process 的那組。

四個玩家

  • Node.js(2009-) by Ryan Dahl。第一個把 V8 嫁接到 server 環境、開出整個 npm 生態的東西,現在還是業界預設。包袱:CommonJS(require)與 ESM(import)並存的歷史傷疤、node_modules 大、TS 一度只能靠外掛工具編。LTS 走 Node 20 → 22 → 24
  • Deno(2020-) by Ryan Dahl(同一個人,“10 things I regret about Node.js” 演講後做的「do-over」)。賣點:Web 標準 API 優先(fetch / Streams / Crypto 內建)、TypeScript 原生不必編、有 permission model(明確允許才能讀檔/連網)、單一執行檔。Deno 1 時代生態小、跑不了大量 npm 套件是最大痛點;Deno 2(2024/10) 起原生理解 package.jsonnode_modules、支援 npm workspace,可用性大幅改善。
  • Bun(v1.0 於 2023/09 by Jarred Sumner。引擎換成 JavaScriptCore(Safari 那顆,不是 V8),主打「比 Node 快數倍」,並把 runtime / package manager / bundler / test runner 整套綁在一個 binary:你裝一個 Bun,dev loop 一條龍。代價:仍在補齊 Node 相容性、生態比 Node 小、node_modules 邊角偶有相容洞。
  • Edge runtime——Cloudflare WorkersVercel Edge FunctionsDeno Deploy。這不是「再一個 Node」,是部署型態:把 V8 切成輕量 isolates 撒到全球節點、冷啟動毫秒級、按請求計費。代價:API 大幅縮減(沒有檔案系統、沒有 long-running process、執行時間和記憶體有上限),只能用 Web 標準 API 寫。

值得學的 host API(學 runtime,很大一部分就是學這些)

既然引擎大同小異、差別幾乎全在 host API,那**「學會一個 runtime」很大一部分就是「學會它的 host API」**。下面這幾類是最該熟的——而且它們天然分成「Node 專屬」與「Web 標準」兩半,剛好就是「能不能跨 runtime / 能不能上 Edge」的分界線:

Host API做什麼跨 runtime 可攜性
node:fs讀寫檔案系統Node/Deno/Bun ✓、Edge ✗
node:child_process跑別的程式(AI agent 在本機跑指令的底層Node/Deno/Bun ✓、Edge ✗
node:http / node:net開 HTTP server、TCP socketNode ✓、Deno/Bun 走相容層、Edge ✗
node:processenv / argv / stdin·stdout / exit codeNode/Deno/Bun ✓、Edge 受限
Node Streams + Buffer串流大資料、pipe;二進位Node ✓;跨 runtime 建議改用下面的 Web 版
node:crypto雜湊、簽章、加解密Node/Deno/Bun ✓;跨 runtime 改用 Web Crypto
fetch / Request / Response發/收 HTTP(Web 標準)全部 ✓
Web Streams(ReadableStream…) + Uint8Array串流、二進位(Web 標準)全部 ✓
Web Crypto(crypto.subtle雜湊、簽章(Web 標準)全部 ✓
AbortController / AbortSignal取消請求 / 操作 / timeout(Web 標準)全部 ✓

讀法:上半部(node: 命名空間)就是 Edge 沒有、跨 runtime 容易出事的那群;下半部(Web 標準、瀏覽器也有)才是「換 runtime 不換 code」的本錢。 所以學 host API 和學「runtime 可攜性」根本是同一課的兩面——下一節就講為什麼這兩年值得特別押下半部。

Web 標準收斂——這兩年最重要的事

2024–2026 年最大的變化不是「誰最快」,而是所有非瀏覽器 runtime 同時把 fetch、Streams、Request / ResponseAbortController、Web Crypto 等 Web API 補齊。這件事由 WinterTC 在推。為什麼這對你重要:

  • 同一段 code 越用 Web 標準 API、越能跨 runtime 跑。fetch 而不是 Node 的 http.request、用 Request / Response 而不是 Express 的 req / res,你的 handler 在 Node、Deno、Bun、Edge 上行為一致。
  • 這也解釋了 React SSR 為什麼有兩個串流 API。 renderToPipeableStream 用 Node Stream,只能在 Node 跑renderToReadableStream 用 Web Streams,Edge / Deno / Bun 都能跑。兩個 API 不是冗餘、是 host 差異逼出來的。
  • 越靠 Web 標準,越能「換 runtime 不換 code」。 想從 Node 遷到 Bun、從 Vercel 換到 Cloudflare,這條捷徑就在這。

常見混淆:runtime ≠ package manager ≠ bundler

這三層被 Bun 綁在一起後,初學者常分不清:

  • Runtime——跑 JS 的引擎 + host:Node、Deno、Bun、Edge。
  • Package manager——管 node_modules:npm、pnpm、yarn、bun。它們本身都是「跑在 Node 上的程式」(bun 例外,因為內建在 Bun 裡)。
  • Bundler——把多檔 JS / TS 打成可發布的包:Vite、esbuild、Rolldown、Webpack。也都是跑在 Node(或 Bun)上

Bun 特別之處是它一個 binary 把這三件事全做了。便利歸便利——但別把「bun installnpm install 快」當成「Bun runtime 比 Node runtime 快」,這是兩件事。

TypeScript 是「runtime 自己解」還是「工具鏈解」

「我寫 TS 能不能直接跑」這件事,現在分裂在 runtime 層:

  • Nodev22.6 開始實驗性支援 type stripping、v22.18 / v23.6 起預設可以直接 node script.ts、v24.12 / v25.2 後標為 stable。但 Node 的做法只是把型別擦掉——不做型別檢查,你還是要靠 tsc --noEmit 才有檢查。
  • Deno / Bun:原生跑 .ts,零配置。

附註:真要選,怎麼選

理解了上面那層,選型反而是小事——而且多數時候根本不用選:沒有特殊理由就用 Node,最大生態、最多職缺、你會踩的坑都有人踩過、雲端與教學文件預設都是它。真正會把你推離 Node 的是少數情境:

  • 想要 TS / runtime / PM / test 一條龍、願承擔較新框架風險 → Bun。 比 Node 快不是錯(多數 micro-benchmark 確實如此),但「快」幾乎從來不是選型最痛的點;真正要賭的是相容性——你某個 npm 相依(或它依賴的某個 Node API)在 Bun 上行為跟 Node 不完全一樣,而你往往是等它壞掉才發現。生態夠主流的套件多半沒事,吃 Node 底層或冷門原生模組的才是地雷。
  • 要 Web 標準乾淨派、想要 sandbox / 權限模型 → Deno。 Deno 2 後可用度大幅改善,但生態仍小一圈,主要看你受不受得了「比 Node 邊緣套件少」。
  • 要全球 low-latency、低冷啟動 → Edge runtime。 共通前提是它走 Web-only 的 isolate 模型:通常沒有真正的檔案系統、不適合長跑 process。但確切的限制和計費方式(CPU/wall time 上限、記憶體、可用的 API、價格、分不分方案)每家差很多——別記死任何數字,挑定哪家後直接讀那家的文件,例如 Cloudflare Workers limitsVercel Functions limitsDeno Deploy

怎麼入手

runtime 是個底層概念,與其塞進一個大 session 一次做完,不如拆成幾個各自獨立的 agent session,順序也有意義:先讀懂概念 → 把 runtime 裝好設好 → 再用幾個真實小專案,分頭把最常用的 host API 各摸一遍。下面每段 code block 都是一個獨立 session 的 prompt,開新對話、照順序貼給 agent。

第 1 步:把「runtime 這一層」讀懂(一個 session)

這篇給了「JS 引擎 + host API」的骨架,建議至少再從外部把同一個概念走一次才扎實。要找的是針對「runtime 這層」的資源——不是泛論 JS 語言、不是 event loop / async 這些相關但更上層的主題。找資料時作者的權威性比「講得淺白」更重要:簡化沒問題,要是「真懂之後的簡化」,不是含糊的近似。對「JS 寫得熟、但還沒蹲過 server-side 底層」的人來說,下面兩篇權威入門剛好互補——一個是中文 narrative、一個是官方參考:

  • Huli — 從「為什麼不能用這個函式」談執行環境(runtime)(資深前端工程師 Huli;亦有英文版)。從「在 browser 跑 btoa 沒事、在 Node 卻報 btoa is not defined」這個具體故事切入,順勢走進「JS 核心、browser host、Node host」的分層——是中文圈最好的入門 narrative。
  • MDN — JavaScript execution model(Mozilla 官方參考;Mozilla 自己維護 SpiderMonkey 引擎)。開頭第一段就用本文同一組詞名稱對齊:「JavaScript execution requires the cooperation of two pieces of software: the JavaScript engine and the host environment.」當英文錨點兼字彙對齊。

想再深一格:Cloudflare — How Workers works,從 V8 isolate 角度補一筆「為什麼 Edge 也是一種 runtime、但跟 Node 不同層」。

👉 複製這段 prompt 開一個 session:

我想真正理解「JavaScript runtime」這層——尤其「JS 引擎 + host API」的分層,
以及為什麼 Node / Deno / Bun / Edge 是同一個概念的不同實作。請帶我做這件事,
不確定就先問我:
1. 讓我自己先讀下面兩篇權威入門(請不要憑記憶複述、不要先總結,我讀完再來討論):
- Huli: https://blog.huli.tw/2022/02/09/javascript-runtime/
- MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model
如果你判斷還需要 1–2 個權威、不誤導、針對「runtime 這層」(不是 event loop /
async)的資源來補強,列出來並各附「為什麼可信」一句話——作者要在 runtime 領域有
verifiable 的深度(runtime / 引擎核心貢獻者、官方標準文件、研討會講者之類),
別給內容農場或 AI 拼湊的文章。
2. 我讀完後,用一段話跟我對齊重點:runtime = JS 引擎 + host API、為什麼差別幾乎
全在 host API、這個分層怎麼直接解釋「同一段 code 在某個 runtime 跑得了、在另一個
跑不了」——例如 Cloudflare Workers 沒有 fs、Node 與 Bun 的 npm 邊角相容性、
React SSR 為什麼有兩個串流 API。
3. 出幾題問我(「V8 ≠ Node」是什麼意思、Bun 換 JSC 對 Node 相容的影響、為什麼 Edge
的限制不能用「再加些 Node API 就好」解決),我答完後依官方文件或上面列的權威資源
(不要憑你的記憶)幫我訂正。
若這個 session 過長、方向變過幾次,或你判斷 context 壓縮可能影響你的理解,提醒我:換新 session 通常會更穩、更準。提醒時一併把目前狀態、決策、待辦、關鍵檔案路徑整理成一份專案內的交接 Markdown 給下一輪 agent 用;沒有合適專案資料夾或任務很輕,就直接把摘要貼進新對話。不要頻繁提醒,只在真的會影響品質時提。

第 2 步:把 runtime 裝好、設好(一個 session)

概念有了,先把環境弄對再寫 code。runtime 的裝法這幾年一直在變(版本管理器、LTS 政策、原生 TS 支援陸續變動),別照舊文章硬裝——讓 agent 查當下官方與社群最推薦的做法,並先看你機器上已經有什麼、有沒有打架。

👉 複製這段 prompt 開一個 session:

我要把一個 JS runtime(Node / Deno / Bun,請先幫我評估或問我該用哪個)裝好、設定好,
當作後面練習的環境。請依「當下最新」的官方與社群建議帶我,不要照你記憶中的舊裝法,不確定就問我:
1. 先看我機器現況:幫我跑並解讀 `node -v`、`which -a node`、有沒有裝 nvm / fnm / Volta、
有沒有 Homebrew 裝的 node 跟版本管理器打架、PATH 有沒有問題。
2. 查目前官方 + 社群最推薦的裝法並給我推薦與理由:例如 Node 該用版本管理器(nvm / fnm / Volta)
還是官方安裝檔、該裝哪個 LTS、要不要開原生 TS 執行;若選 Deno / Bun 則各自的官方裝法。
3. 帶我裝好、設好一個乾淨基準(版本管理器、預設版本、確認能不能直接 `node script.ts`),
每個設定都說明在幹嘛。
4. 出兩題問我(為什麼用版本管理器而不是系統全域裝一個、LTS 跟 Current 差在哪),依官方文件訂正。
若這個 session 過長、方向變過幾次,或你判斷 context 壓縮可能影響你的理解,提醒我:換新 session 通常會更穩、更準。提醒時一併把目前狀態、決策、待辦、關鍵檔案路徑整理成一份專案內的交接 Markdown 給下一輪 agent 用;沒有合適專案資料夾或任務很輕,就直接把摘要貼進新對話。不要頻繁提醒,只在真的會影響品質時提。

第 3 步:用真實小專案,分頭把常用 host API 摸過(每個 POC 一個 session)

這步是重點,規劃方式刻意反過來想:先列出「初學者非懂不可的 host API 有哪幾群」,再為每群挑一個最能讓人體會它在解什麼的真實小專案。下表就是這樣排出來的——每個 POC 是一個獨立 session,挑你還不熟的那群做就好,不必全做

真實小專案(POC)會摸到的 host API它逼你搞懂的核心觀念
檔案處理器 / 迷你 bundler(讀一堆檔 → 轉換 → 輸出)fs / fs/promisespath、Node Streams檔案系統、同步 vs 非同步readFileSync 為何卡死 server)、用串流處理大檔
Express 式 HTTP 伺服器 + 路由node:http(或 Bun.serve)、Request / Responseserver 怎麼收發請求、路由、handler 天生非同步
網頁爬蟲(web scraper)全域 fetchAbortController、Web Streams、Web Crypto對外網路呼叫、並發控制Promise.all vs 逐一 await)、timeout 取消、用 hash 去重
包一個指令的 CLI dev tool(lint / format runner 之類)process(argv / env / stdio)、child_process.spawn程式怎麼讀參數與環境、跑子程序並即時串流它的輸出——AI agent 在本機跑指令的底層

下面是四個 POC 的完整 prompt。每個是一個獨立的 agent session,挑你想做的那個複製整段去開新對話;不必全做。

POC ① 檔案處理器 / 迷你 bundler

👉 複製這段 prompt 開一個 session:

我想透過做一個最小的「檔案處理器 / 迷你 bundler」——讀進一個資料夾的多個 .ts 檔、
做簡單轉換(譬如把 import 接成單一字串)、寫出一個合併檔——來搞懂 JS runtime 裡跟
「檔案系統」相關的 host API:fs / fs/promises、path、以及 Node Streams。
不確定就先問我:
1. 入門:用一段話講這幾個 host API 各自在解什麼問題;尤其把「為什麼 server 端不該用
readFileSync」(同步呼叫卡住 event loop、整個 process 停在這裡)這件事講清楚。
2. 動手 v1:帶我從零做到能跑——讀資料夾、逐檔處理、寫出 bundle。第一版**故意用同步
API**(readFileSync / writeFileSync),先跑通。
3. 進階:把同步版改成非同步(fs/promises)。然後用一個「會卡」情境讓我親眼比較兩者
差別——例如在同支腳本裡也起一個小 HTTP server,跑同步版時 server 對請求停止回應、
跑非同步版時不會。
4. 大檔:當輸入是數百 MB 的單檔,readFile 把整檔讀進記憶體都不划算——帶我把讀寫改成
Node Streams(createReadStream / createWriteStream + pipe),講清楚 stream 解決的是
哪個問題、什麼時候根本不用 stream。
5. 可攜性:指出這支工具哪些段只能在 Node 跑、為什麼 Edge runtime 不可能跑——並用這個
例子說明「沒有 fs」這條限制不是 API 還沒補,是 sandbox 模型決定的。
6. 檢查:出幾題問我(readFileSync 為什麼在 server 是地雷、stream 解決的是哪個問題、
path.join 為什麼不該用字串相加代替),我答完後依 Node 官方文件
(不要憑你的記憶)幫我訂正。
若這個 session 過長、方向變過幾次,或你判斷 context 壓縮可能影響你的理解,提醒我:換新 session 通常會更穩、更準。提醒時一併把目前狀態、決策、待辦、關鍵檔案路徑整理成一份專案內的交接 Markdown 給下一輪 agent 用;沒有合適專案資料夾或任務很輕,就直接把摘要貼進新對話。不要頻繁提醒,只在真的會影響品質時提。

POC ② Express 式 HTTP 伺服器 + 路由

👉 複製這段 prompt 開一個 session:

我想透過從零做一個最小的「Express 式 HTTP 伺服器 + 路由」(**不用 Express、不用任何
framework**)——支援幾條路由(GET /、GET /api/items、POST /api/items)並回 JSON——
來搞懂 JS runtime 裡跟「server 收發網路請求」相關的 host API。不確定就先問我:
1. 入門:用一段話講一個 HTTP server 在 runtime 裡到底在做什麼——listen socket、接連線、
parse 出 request、把 response 寫回去——以及為什麼 handler 幾乎都是 async。
2. 動手 A:用 Node 的 `node:http` 從零寫一遍。自己 parse URL、用 if / switch 寫一個小
路由表、用 JSON.stringify 回 response,讓我看到「沒有框架時 handler 長什麼樣」。
3. 動手 B:把同一支 server 改寫成 Web 標準的「fetch handler」形式——一個
(request: Request) => Promise<Response>——部到 Bun(用 Bun.serve)或 Deno(用 Deno.serve)跑。
4. 對照:並排告訴我 (A) `node:http` 是 Node 專屬 host API;(B) `Request` / `Response`
是 Web 標準、跨 runtime 可攜;後者也是 Cloudflare Workers / Vercel Edge / Next.js
Route Handlers 的同一個模型——一旦寫成這形狀,code 幾乎不用改就能上 Edge。
5. 親手感受單執行緒:在 handler 裡故意塞一個 5 秒的同步 CPU 迴圈,用 ab / curl 連發
幾個請求看會發生什麼——讓我親眼看到「一個 handler 卡住 = 整個 server 卡住」。
6. 檢查:出幾題問我(為什麼 `node:http` 的 server 不能直接上 Cloudflare Workers、
Request / Response 的好處到底是什麼、handler 為什麼幾乎都是 async),我答完後依
Node、Bun、MDN 官方文件(不要憑你的記憶)幫我訂正。
若這個 session 過長、方向變過幾次,或你判斷 context 壓縮可能影響你的理解,提醒我:換新 session 通常會更穩、更準。提醒時一併把目前狀態、決策、待辦、關鍵檔案路徑整理成一份專案內的交接 Markdown 給下一輪 agent 用;沒有合適專案資料夾或任務很輕,就直接把摘要貼進新對話。不要頻繁提醒,只在真的會影響品質時提。

POC ③ 網頁爬蟲

👉 複製這段 prompt 開一個 session:

我想透過做一個最小的「網頁爬蟲」——給一份 URL 清單,逐一抓回 HTML、抽出 <title>、
存成 JSON——來搞懂 JS runtime 裡跟「對外網路呼叫」相關的 Web 標準 host API:
全域 fetch、AbortController、Web Crypto。不確定就先問我:
1. 入門:用一段話講為什麼這幾個 API 是 Web 標準(瀏覽器、Node、Deno、Bun、Edge 都有),
以及「同步 fetch」為什麼根本不存在——能用 fetch 是因為 runtime 給了 event loop +
promise 排程。
2. 動手 v1:用最樸素的方式做出來——一個 for loop、每個 URL 逐一 await fetch,
抓 response.text()、用 regex 抽 <title>。先跑通。
3. 並發控制:把所有 fetch 改成一次發出(Promise.all),我跑一次告訴你結果;然後帶我
加「同時最多 N 個」的並發上限,並解釋「all at once」、「逐一」、「有上限的並發」這
三種在真實世界各會踩到什麼問題(rate limit、總時長、記憶體 / file descriptor 爆掉)。
4. Timeout / 取消:每個 fetch 加 3 秒 timeout,**親手用 AbortController + setTimeout
寫一次**(不要包好的 library),讓我看清 AbortController 是泛用的取消機制、不是
fetch 的選項。
5. 去重:用 Web Crypto 的 `crypto.subtle.digest()` 對每頁內容算 SHA-256,把「不同網址
但內容一樣」的頁面去重;順便讓我認識 Web Crypto 跟 Node 的 node:crypto 是兩個不同
的 API、什麼時候該用哪個。
6. 可攜性實驗:整支爬蟲幾乎全用 Web 標準。帶我把它原封不動丟到 Bun 跑、再部到一個
Cloudflare Worker 上跑——讓我親眼看到「code 不改、跨 runtime 直接跑」。
7. 檢查:出幾題問我(為什麼沒有同步 fetch、Promise.all 的失敗模式、AbortController 跟
timeout 的關係、Web Crypto 為什麼是 async),我答完後依 MDN、Node、Cloudflare
Workers 官方文件(不要憑你的記憶)幫我訂正。
若這個 session 過長、方向變過幾次,或你判斷 context 壓縮可能影響你的理解,提醒我:換新 session 通常會更穩、更準。提醒時一併把目前狀態、決策、待辦、關鍵檔案路徑整理成一份專案內的交接 Markdown 給下一輪 agent 用;沒有合適專案資料夾或任務很輕,就直接把摘要貼進新對話。不要頻繁提醒,只在真的會影響品質時提。

POC ④ 包一個指令的 CLI dev tool

👉 複製這段 prompt 開一個 session:

我想透過做一個最小的「CLI dev tool」——譬如包一個 typecheck runner,跑
`mytool ./src` 會找這個資料夾的 tsconfig.json、起一個 child process 跑 `tsc --noEmit`、
把錯誤即時印出來——來搞懂 JS runtime 裡跟「程式怎麼讀參數、怎麼跑別的程式」相關的
host API:process、child_process.spawn、stdio 的串流。**這正是 AI agent 在本機跑指令
的縮影。** 不確定就先問我:
1. 入門:用一段話講 process 物件給了你什麼(argv / env / cwd / stdin / stdout / stderr /
exit code),以及 child_process 裡 spawn / exec / execSync 的差別——為什麼幾乎永遠該用
spawn(不要 buffer 全部輸出)。
2. 動手:用 process.argv 解參數、用 process.env 讀環境變數、用 child_process.spawn 起
一個 child 跑 `npx tsc --noEmit`,並把 child 的 stdout / stderr **即時** pipe 到自己的
process.stdout / process.stderr——讓使用者看到 tsc 的輸出一行一行冒出來,不是等整個
跑完一次吐。
3. Timeout / 取消:加一個 --timeout 參數,用 AbortController(spawn 接受 signal)讓子
程序超時就被中止;child 結束後依它的 exit code 決定自己的 exit code。
4. 親手感受 buffer 為什麼壞:拿一個會輸出超大量 stdout 的指令(譬如一個故意印百萬行
的腳本)打進去,先跑串流版;接著請你解釋如果改用 execSync 或 exec、為什麼這支工具
會把記憶體吃光甚至 OOM。
5. AI agent 視角:請告訴我這個結構(讀 args → 起 child → 串流 stdout → 控 timeout →
處理 exit code)正是 Claude Code / MCP server / agent 在本機跑指令的同一個 host API
組合,也是 Edge runtime「永遠」不可能跑這種工具的原因——不是 API 沒補,是 sandbox
模型不允許 spawn 子程序。
6. 檢查:出幾題問我(spawn vs exec 為什麼選 spawn、為什麼 child 的 stdout 要 pipe 不要
等全部 buffer 完、process.exit() 為什麼可能截斷還沒寫完的 stdout),我答完後依 Node
官方文件(不要憑你的記憶)幫我訂正。
若這個 session 過長、方向變過幾次,或你判斷 context 壓縮可能影響你的理解,提醒我:換新 session 通常會更穩、更準。提醒時一併把目前狀態、決策、待辦、關鍵檔案路徑整理成一份專案內的交接 Markdown 給下一輪 agent 用;沒有合適專案資料夾或任務很輕,就直接把摘要貼進新對話。不要頻繁提醒,只在真的會影響品質時提。

延伸主題

  • React + TypeScript + SSR:JS runtime 最常被用到的場景。SSR 的整個討論——從 raw Vite 接 SSR、到 Next.js 的快取模型、到 Edge 部署的限制——其實都建在這篇的「runtime 是什麼」之上。
  • Web app architecture:runtime 只是 production stack 裡的一個部分,這頁把其他部分(DNS、CDN、LB、DB、object storage、queue、secrets、observability)鋪開——一個人怎麼把一個 web app 端到端送上雲。讀完 runtime、想知道「我寫的這段 code 上線那天還會跟誰共處」,這頁是地圖。
  • 回頭補「引擎」那半(選讀):Lydia Hallie — JavaScript Visualized: the JavaScript Engine(知名 JS 視覺化教學作者,以 Keynote 手繪動畫拆解 JS internals)。本篇刻意把引擎當成大同小異的黑盒、火力全押在 host API;但若「引擎到底在 parse、compile、執行什麼,heap、call stack 是什麼」對你其實還是一團模糊,這篇圖解是最好的補課。(偏好影片、想把 JS 底層一次看過的話,她的個人頻道 @theavocoder 有整個 JavaScript Visualized 視覺化系列——execution context、closures、event loop、promise 等;範圍比本篇的「引擎 + host API」更廣,偏 JS 語言內部與 async 排程。)