前端 system design:從載入到上線的取捨清單
文字解析 · 1 支影片 · 產生於 2026-10-01 00:54
章節(13)
1. Outline
- 起點 · Frontend System Design at Scale (The Senior Dev Playbook)
用「使用者打開網頁之後會發生什麼」這條軸,把前端 system design 的十三個面向串成一條推理鏈:先把程式碼送到瀏覽器(bundling → micro-frontend → repo 策略 → module federation → CDN → bundle 最佳化),再處理它跑起來之後的事(state 分類 → 即時同步),最後是怎麼安全上線與壞掉時怎麼撐住(CI/feature flag → error boundary/監控),收在「先問限制再提方案」。
2. YouTuber 的思維推導
Frontend System Design at Scale (The Senior Dev Playbook)
Monsterlessons Academy · 7m12s · 字幕 en · vision=on (使用者指定 --vision true;影片本身是投影片型,程式碼與架構圖直接承載論點(lodash import 對照、CDN 延遲圖、state 三分表),不看圖會漏掉作者真正的證據。) · YouTube
預估 vs 實際耗時
| 階段 | 預估 | 實際 |
|---|---|---|
| fetch | 6s | 1s |
| segment | 44s | – |
| shot | 23s | 3s |
| analyze | 3m45s | – |
| render | 5s | – |
作者的推導有一條清楚的主軸:他不是在教「前端 system design 有哪些題目」,而是在示範「資深工程師怎麼把一個需求推到底」。他先立下前提——這是開放題,面試官買的是思考過程而不是名詞——接著沿著「使用者打開網頁之後會發生什麼」一路往下推:先是檔案怎麼送到瀏覽器(bundling → 切 chunk),再把切割的刀口從檔案提升到團隊(micro-frontend),拆開之後自然要問程式碼放哪(monorepo / multi-repo)、執行期怎麼接回來(module federation)、這些片段從哪裡下載(CDN 與快取)、下載的東西本身夠不夠瘦與執行順序對不對(tree shaking、defer)。檔案進到瀏覽器之後,主題轉向 runtime:資料怎麼分類與管理(三種 state)、資料如果是伺服器主動推來的又該怎麼辦(WebSocket 與斷線重連)。
最後兩層是把這一切送上線並維持住:CI、feature flag 與 A/B testing 決定「怎麼安全地改」,error boundary 與監控決定「壞掉時怎麼撐住並修好」。收尾時他把整條鏈折回起點:既然每個環節都有取捨,那麼正確的第一步不是給答案,而是先問限制。每一段的追問(followup)是這支影片的隱藏結構——他反覆示範資深與中階的差別不在知道名詞,而在知道每個選擇的代價。
推理鏈:每段留給下一段的線索
每一列是一段,箭頭後的字是這段留給下一段的線索。點段落標題跳到該段。
- 面試不是背答案,是給思路 → 既然評的是思路而不是答案,那就產生一個新問題:一道沒有標準答案的題目,要從哪裡開始講?
- 前端 system design 是開放題 → 清單要從哪一項開始?作者選了使用者打開網頁後第一件真正發生的事——把 JavaScript 檔案載下來。
- Bundling:把大檔切成 chunk → chunk 切的是「檔案」這一刀。但如果要切的不是檔案,而是「哪個團隊負責哪一塊」,同一把刀還管用嗎?
- Micro-frontend:切團隊也切複雜度 → 既然要讓各團隊互不干擾,下一個立刻要決定的問題是:這些拆開的程式碼到底該放在幾個 repository 裡?
- Monorepo vs multi-repo → 程式碼放哪解決了,但拆開的片段在使用者瀏覽器裡是怎麼被重新組回一個畫面的?
- Module federation:各自部署 → 那份 remoteEntry.js 是從一個 CDN 網址載下來的——CDN 到底幫我們省下了什麼,值得單獨談一段。
- CDN 與快取:距離就是延遲 → CDN 縮短的是傳輸距離,但檔案本身太肥、或載入順序不對的話,使用者還是在等——問題得回到 bundle 的內容本身。
- Bundle 最佳化與瀏覽器載入順序 → 檔案終於快速地載進瀏覽器並跑起來了——接下來要處理的是它跑起來之後手上那些資料該怎麼管。
- State management:三種 state → server state 的模型假設是「我需要時去拉」。但如果資料是伺服器主動推過來的,這個模型就不夠用了。
- 即時同步與斷線後怎麼辦 → 架構、資料與即時性都設計好了,但這一切要能持續進到正式環境才有價值——接下來是程式碼怎麼被安全地送上線。
- CI、feature flag 與 A/B 測試 → 程式碼安全地上線了,但正式環境一定會壞——最後要處理的是壞掉的時候怎麼撐住並修好。
- 可靠性:不要整頁白畫面 → 技術面到此走完一輪。但一場面試講不完十三項——所以最後要回答的是:面對一道題,你要先挑哪一項來講?
- 先問限制,再提方案
3. 逐段說明
Frontend System Design at Scale (The Senior Dev Playbook)
1. 面試不是背答案,是給思路 0:00–0:31
作者開場點出:要通過資深面試、要真的做好資深工作,都必須理解什麼是 system design、怎麼做出可擴展的應用。他觀察到很多人在面試時「只是在回答問題」,對 senior 職位來說這樣不夠。這支影片要教的是怎麼回答 system design 問題,以及該講些什麼。
推理作者開場先把評分標準講清楚:他觀察到很多人在面試裡「只是在回答問題」,而這對 senior 職位不夠。因為如果評分標準是「答對」,準備方式就是背清單;他要先推翻這個假設,後面所有內容才有意義——他要教的不是清單,是怎麼講。
AI 補充作者沒有明說「只是在回答問題」和「展現思路」差在哪,但這是整支影片的判準,值得先補上:回答問題是「有沒有這個東西」的是非題(「你會用 CDN 嗎?」「會」),展現思路是「在什麼條件下我選它、代價是什麼」的取捨題。面試官問 system design 通常沒有標準答案可對,他真正在觀察的是你會不會自己界定問題、會不會主動指出方案的缺點。這也解釋了為什麼同一份技術清單,中階講出來像背誦、資深講出來像決策紀錄。
術語:senior developer
2. 前端 system design 是開放題 0:31–0:54
前端的 system design 沒有單一正確答案。面試官要看的是你怎麼思考:怎麼實作一個功能、一個專案,或怎麼把功能擴展出去。接下來作者要逐項談可擴展前端架構最重要的原則。
推理因為上一段把評分標準從「答對」換成「展現思考」,所以作者這裡必須先定義題型:前端 system design 是開放題,你要展示的是怎麼實作一個功能、一個專案,或怎麼把既有功能擴展出去。定義完題型,他才有立場宣布接下來要走一份「可擴展前端架構最重要的原則」清單——這份清單不是答案本身,是思考時可以掃描的面向。
AI 補充這裡有個容易被忽略的轉折:作者說沒有單一答案,卻馬上開始列清單,看起來矛盾。實際上清單的角色是「檢查面向」而不是「標準答案」——面試時你不會把十三項全講完,而是用它掃描哪幾項跟眼前這題的限制有關。值得注意他排清單的順序不是隨機的,而是跟著使用者的實際體驗走:先是程式碼怎麼送到瀏覽器,再來是送到之後怎麼跑、怎麼維持。用這個順序記,比按技術分類記更不容易漏。
術語:scalable architecture
3. Bundling:把大檔切成 chunk 0:54–1:21
第一個一定要提的是 bundling。把整份前端 JavaScript 打包成單一檔案並不快,5 MB 以上的檔案載入太久。正解是 lazy loading:把巨大的 bundle 切成獨立 chunk,每個頁面只載入自己需要的那塊。

推理因為上一段決定要照「使用者實際遇到的順序」走,所以作者從 bundling 起手:把整份前端 JavaScript 打包成一個檔案並不快,5 MB 以上載入太久。他接著給出對應的解法——lazy loading 加上把大 bundle 切成獨立 chunk,每頁只載自己要的那塊。這一段建立了整支影片反覆出現的句型:先指出一個會隨規模惡化的問題,再給一個把「一整塊」拆成「按需要的小塊」的解法。
- CCode splitting:用動態 import 把大 bundle 切成獨立 chunk,每頁只載要用的那塊→ 畫地圖
- EReact.lazy(() => import('./Dashboard')) 讓 Dashboard 被單獨切成一個檔案→ 存+演練
- P把自己專案裡最大的一個路由改成動態 import→ 練習
AI 補充投影片給的是 React.lazy 加動態 import 的兩行寫法,這裡值得補上它為什麼有效:`import('./Dashboard')` 這種動態語法在 build 階段就是給打包工具的切割記號,Vite 或 webpack 看到它就把 Dashboard 及其相依單獨輸出成一個檔案,執行期真的走到那一行才發請求。也就是說切割發生在建置期,決定要不要載發生在執行期,兩件事的分工要講清楚。另外作者的「5 MB」不必當成硬門檻——真正該看的是使用者在目標網路環境下等多久,行動網路上 1 MB 的 JavaScript 就足以讓首屏明顯變慢,因為 JS 除了下載還要解析和執行。實務上先切的通常是路由層級(每個頁面一個 chunk),再視情況切到元件層級(例如很少開的圖表或編輯器)。
術語:code splitting
4. Micro-frontend:切團隊也切複雜度 1:21–1:56
第二點是 micro-frontend:把前端應用拆成獨立部分,通常放進不同 repository,由不同團隊各自開發,彼此不互相干擾。代價是把這些部分接回去的複雜度變高。作者強調這只在專案夠大時才划算,幾個頁面的簡單應用不該這樣做。

推理因為 chunk 只解決載入量、解決不了「二十個人改同一份程式碼會互相踩腳」,所以作者接著提 micro-frontend:把前端應用拆成獨立部分,通常各自放進不同 repository,由不同團隊各自開發、互不干擾。他緊接著自己踩煞車——這樣做會提高把各部分接回去的複雜度,只有專案夠大才划算,幾個頁面的應用這樣做沒有意義。這個「先給好處再自己給代價」的動作,就是他第 1 段講的資深表現的示範。
AI 補充投影片的架構圖把重點畫得很直接:一個 Shell App (host) 裡面放著 Search(A 團隊)、Recommendations(B 團隊)、Cart(C 團隊)。注意框線切的是團隊,不是技術層級——這是 micro-frontend 最容易被誤解的地方,它是一個組織問題的技術解法,不是效能最佳化手段。作者說「複雜度上升」但沒展開,實際的代價相當具體:共用的 React 版本必須協調(否則同一頁載入兩份 React),跨片段的路由與登入狀態要有共同約定,設計系統要抽成共用套件否則畫面會不一致,端對端測試要跨多個部署才跑得起來。判斷門檻可以用一個簡單問題代替「專案夠不夠大」:你們是否已經因為多個團隊搶同一條發布流程而互相等待?沒有這個痛,micro-frontend 買來的就只有成本。
5. Monorepo vs multi-repo 1:56–2:32
承接 micro-frontend 的追問:把前端拆到不同 repository 就是 multi-repo;把所有模組放在同一個 repository 就是 monorepo,可以用 Nx 之類的工具管理。作者的重點不是名詞本身,而是資深工程師必須解釋「為什麼」以及兩者的差異。

推理因為拆開之後 repository 的擺法變成一個真的要選的問題,所以作者把兩個選項並排:拆到不同 repository 是 multi-repo,全部模組放在同一個 repository 是 monorepo,可以用 Nx 這類工具管理。然後他把這一段的重點從技術拉回第 1 段的判準——資深工程師必須解釋為什麼以及兩者差在哪,而不是只報出這兩個名詞存在。
AI 補充投影片的對照表比口述多給了關鍵的一筆:multi-repo 那一欄標了 versioning hell,並附上實際流程——app-a 要修 shared-ui 的 bug,得開 PR、等發版、升版號,要花好幾天;monorepo 則是一個 PR 就解決。這句註解才是真正的取捨核心:multi-repo 換來的是團隊自主,付出的是跨套件改動的延遲;monorepo 換來的是一次改完,付出的是共用一條 CI、共用工具鏈,以及需要 Nx/Turborepo 這類工具做增量建置,否則改一行就全部重跑。要注意 monorepo 和 micro-frontend(見第 4 段)是兩個獨立的軸,可以自由組合:monorepo 裡放多個可獨立部署的片段是很常見的組合,同一份程式碼庫、各自的發布流程。作者這裡口語上把 monorepo 說成了「monolith versus multi-repo」,看投影片就知道他要對比的是 monorepo。
6. Module federation:各自部署 2:32–2:55
第二個追問是 module federation——把前端切成不同片段、之後在單一專案裡載入的具體機制。關鍵好處是 A 團隊可以部署自己那塊的新版本,主應用會直接吃到新版,不需要所有片段一起重新部署。

推理因為 repository 的擺法決定不了執行期怎麼合體,所以作者把 module federation 當成第二個追問端出來:它正是把前端切成不同片段、之後在單一專案裡載入的方式。他把價值濃縮成一句——A 團隊可以部署自己那塊的新版本,主應用會直接吃到,不必所有片段一起重新部署。這句話正好兌現了第 4 段承諾的「互不干擾」:如果每次改動都要一起發版,拆開就沒有意義。
- CModule federation:host 只記路徑,執行期才去別的團隊抓最新版模組→ 畫地圖
- Amodule federation 像不打包的相依:host 只記路徑,執行期才去抓→ 批判類比
- Ewebpack remotes 設定:cart: 'cart@https://cdn.team-b.com/remoteEntry.js'→ 存+演練
AI 補充投影片的 webpack 設定把機制講得比口述清楚:host 的 `remotes` 裡寫著 `cart: 'cart@https://cdn.team-b.com/remoteEntry.js'`,接著在 React 程式碼裡用 `React.lazy(() => import('cart/CartWidget'))` 使用它。關鍵在那個 URL——host 在建置時並不打包 cart 的程式碼,只記下去哪裡拿;執行期才抓 remoteEntry.js,那是一份 B 團隊產出的清單,說明有哪些模組、實際檔案在哪。所以 B 團隊重新部署、覆蓋掉那個 URL 的內容,host 下一次載入就自動拿到新版,完全不用重 build。注意這裡重複用到了第 3 段的 `React.lazy` 動態 import:同一個語法,先前切的是自家的 chunk,現在跨過網路邊界去拿別人的模組。代價也在同一個地方——那個 URL 掛掉或版本不相容時,host 就開天窗,而且 React 這類共用相依必須在 `shared` 設定裡協調好,否則同一頁會載入兩份。
7. CDN 與快取:距離就是延遲 2:55–3:36
接著是 CDN 與 caching。作者用具體數字說明價值:沒有 CDN 時,東京的使用者要打到美國的 origin server,大約 200 毫秒;配好 CDN 後從東京當地節點、甚至直接從快取拿,只要幾十毫秒。追問則是資產要快取多久、什麼時候要讓快取失效。

推理因為前面幾段產出的所有檔案(chunk、remoteEntry、各片段的程式碼)都得跨網路送到使用者手上,所以作者把 CDN 與快取拉成獨立一項,並且用數字說服:沒有 CDN 時東京的使用者要打到美國的 origin server,大約 200 毫秒;配好 CDN 之後從東京當地節點、甚至直接命中快取,就只要很短的時間。接著他照慣例給出追問——資產要快取多久、什麼時候要讓它失效。
- CCache invalidation:帶內容雜湊的檔案長快取,指路的入口檔要短快取→ 畫地圖
- Echunk 檔名帶內容雜湊可設超長快取,index.html 與 remoteEntry.js 要設極短快取→ 存+演練
- RCDN 為什麼能把延遲從等比降到很低→ 存+回想
AI 補充投影片把兩條路徑並排畫出來,數字是 Tokyo → Virginia 約 200ms、Tokyo → Edge node 命中快取約 20ms。這裡值得補一個作者跳過的因果:延遲的主因不是伺服器慢,而是光速加上 TCP/TLS 的來回次數——距離愈遠,每一次握手都要多花一趟往返,所以縮短物理距離的收益是成倍的,不是線性的。至於他留下的追問,實務上的標準答案是把資產分成兩類:帶內容雜湊的檔名(見第 3 段的 chunk 命名)可以設超長快取,因為內容一變檔名就變;而 index.html 與第 6 段的 remoteEntry.js 這種「指路的檔案」必須設極短快取或不快取,否則使用者會拿著舊地圖去找新檔案。這個組合就是「不可變資產長快取 + 入口檔短快取」,也是為什麼快取失效在前端多半靠檔名而不是靠手動清 CDN。
「it may take just 2 milliseconds」
8. Bundle 最佳化與瀏覽器載入順序 3:36–4:21
再來是 bundle 最佳化。tree shaking 能砍掉沒用到的函式,但前提是 import 寫對——某些寫法會把整個函式庫拉進來,這個好處就沒了;要查問題可以用 webpack bundle analyzer。第二點是理解瀏覽器怎麼執行 JS 與 CSS、何時 paint 與 repaint,所以要懂 defer 之類的手段;作者看過很多正式環境的 script 互相擋住彼此。


推理因為距離已經不是瓶頸,所以作者把注意力轉回檔案內部,講兩件事。第一是 tree shaking:它能砍掉函式庫裡沒用到的函式,但前提是 import 寫對,某些寫法會把整包拉進來、好處就沒了;要查有沒有中招可以用 webpack bundle analyzer。第二是瀏覽器怎麼執行 JS 與 CSS、何時 paint 與 repaint,所以要懂 defer 之類的手段——他說他在正式環境看過大量 script 互相擋住彼此的案例。
- CTree shaking:砍掉沒用到的函式,但 import 寫錯就整包被拉進來、無警告→ 畫地圖
- E同一個 debounce,三種 import 寫法差 70KB+、2KB、可完全搖掉→ 存+演練
- Rdefer/async 只對外部 script 有效→ 存+回想
AI 補充兩張投影片把這段變得可操作。第一張把同一個 `debounce` 的三種寫法並排:`import _ from 'lodash'` 會拉進整包(70KB+),`import debounce from 'lodash/debounce'` 只有 2KB,`import { debounce } from 'lodash-es'` 則因為是 ES module 格式而能被正確搖掉。原因作者沒說:tree shaking 靠的是 ES module 的靜態結構分析,CommonJS 的 `require` 在執行期才決定拿什麼,工具無從判斷哪些能刪——所以 lodash(CommonJS)要靠子路徑 import,lodash-es 才能享受具名 import。第二張則列出四種 script 寫法:無屬性會擋住解析與繪製、`defer` 平行下載並在 HTML 解析完後依序執行、`async` 下載完立刻執行(適合彼此無關的第三方腳本)、critical CSS 直接內嵌以免首屏等外部樣式檔。這正是他說的「script 互相擋住」的解法清單。順帶一提:`defer`/`async` 只對外部 script 有效,寫在內嵌 script 上不會生效,這是實務上很常見的誤用。
術語:render blocking
「We obviously have tree shaking on the client」
9. State management:三種 state 4:21–5:06
狀態管理的重點是分清楚三種 state:global app state、server state、client state,以及各自該用什麼工具。從 API 拿來的 server state 用 React Query 這類函式庫;全域 UI state 可以用 Redux;區域 state 用 useState 就夠。作者提醒要挑適合該業務的工具,很多專案只用 React Query、寫最少的程式碼就夠了。

推理因為前面所有段落談的都是「把程式碼送到瀏覽器」,到這裡主題正式轉入 runtime,所以作者先給分類再給工具:要分清楚 global app state、server state 與 client state,並知道各自該用什麼。從 API 拿來的 server state 用 React Query 這類函式庫,全域 UI state 用 Redux,區域 state 用 useState 就夠。他隨即補上和第 4 段同一種煞車——要挑適合該業務的工具,很多專案只用 React Query、寫最少的程式碼就夠了。
- CServer state:手上永遠只是伺服器資料的一份副本,可能過期→ 畫地圖
- Aserver state 就像快取:本地存的是可能過期的副本,真相在遠端→ 批判類比
- E早年常見錯誤:把 API 資料塞進 Redux,自己寫 loading/error/重新抓取→ 存+演練
- P檢查自己專案有沒有把 API 資料錯塞進全域 store→ 練習
AI 補充投影片的三列表格把分類講得比口述完整:Server State 是 API 來的資料,用 React Query 或 SWR;Global UI State 是 auth、主題、sidebar 開關,用 Zustand 或 Redux;Local UI State 是 modal 開關、輸入框的值,用 useState。這個分類真正的價值在於指出一個歷史誤區:早年大家把 API 資料也塞進 Redux,於是要自己寫 loading、error、快取、重新抓取、race condition 處理——而這些全都是 server state 特有的問題,因為那份資料的真正來源在別人的伺服器上,你手上的永遠只是一份可能過期的副本。React Query 這類工具存在的意義就是把「副本管理」標準化。反過來說,真正的 UI 狀態(modal 開不開)沒有這些問題,用 useState 就夠,硬塞進全域 store 只會製造樣板程式碼。判斷方式很簡單:這份資料的真相在伺服器上,還是只存在於這個瀏覽器分頁裡?
「For global UI state you might want to use Redux」
10. 即時同步與斷線後怎麼辦 5:06–5:36
延伸出來的是即時同步、WebSocket 與 Socket.IO,因為現在很多專案有通知、協作共享、即時儀表板。作者示範可以自己延伸的追問:連線斷掉時你怎麼做?重播漏掉的事件,還是整份重新抓?答案取決於專案需求。

推理因為「去拉」的模型撐不住通知、協作共享與即時儀表板這些現在很常見的需求,所以作者把即時同步、WebSocket 與 Socket.IO 拉出來講。他接著做了一個很值得學的動作:不是等面試官問,而是示範你可以自己延伸的追問——連線斷掉時你怎麼做,重播漏掉的事件還是整份重新抓?然後回到他的標準答案形狀:看專案需求。
AI 補充投影片直接把這兩個追問寫成一張 QUESTIONS 清單,等於在告訴你:這是可以主動端上桌的問題。作者沒說的是這兩條路各自的代價,而這才是面試裡真正能得分的部分。重播(replay)需要伺服器保留事件序號與一段歷史,客戶端重連時帶上最後收到的序號、把缺口補回來——聊天訊息或協作編輯適合這條,因為每個事件本身有意義、漏掉就是資料不見。整份重抓(refetch from scratch)簡單得多,只要重新載入當前快照即可——即時儀表板、股價、線上人數適合這條,因為你只在乎最新值,中間漏掉的舊值本來就沒用。判斷方式其實接得上第 9 段的問題:這些推送是「事件」還是「最新狀態」?另外補一個作者沒提但實務必備的細節:重連要用指數退避,否則伺服器一掛,成千上萬的客戶端會同時重連把它再打掛一次。
術語:event replay
11. CI、feature flag 與 A/B 測試 5:36–6:15
基礎建設與部署也要談:每次有人開 pull request,CI 就要跑 lint、測試與 build,這樣合併前大家才確定程式碼被檢查過。另一項是 feature flags 與 A/B testing——業務常需要漸進推出新功能,或比較哪個版本比較受使用者歡迎,資深工程師必須會實作。


推理因為前面所有設計都要靠一條可靠的發布流程才落得了地,所以作者把基礎建設與部署列進來:每次有人開 pull request,CI 就要跑 lint、測試與 build,這樣合併前大家才確定程式碼被檢查過。接著他把「安全上線」再往前推一步——feature flags 與 A/B testing:業務常需要漸進推出新功能,或比較哪個版本比較受歡迎,資深工程師必須會實作。
AI 補充第一張投影片把口述的三件事展開成一份 ci.yml 的七個步驟:`npm ci`(用 lockfile 安裝並吃快取)、lint、type-check、單元測試、build、e2e、以及 lighthouse 效能預算。最後那項特別值得注意——它把第 3 段和第 8 段談的載入效能變成 CI 會擋下來的硬指標,否則 bundle 的體積只會隨著時間單向惡化,沒有人會主動回頭修。第二張是 YouTube 後台的 A/B 測試報告:三個縮圖標題的觀看時間佔比(23.5%、43.3%、33.2%)與 winner 標記,這是他自己頻道的真實數據。這裡有個作者沒點破但很重要的關聯:feature flag 讓「部署」和「發布」脫鉤——程式碼可以先合併進 main 並部署,但對使用者關著,要開給誰、開多少比例是執行期的決定。這正好解決了第 4 段 micro-frontend 的一個隱憂:獨立部署要能獨立回退,而 flag 就是不用重新部署也能立刻關掉某功能的開關。代價是每個 flag 都是一條分支,清不掉的舊 flag 會讓程式碼難以推理,所以實務上要給 flag 設定到期日。
12. 可靠性:不要整頁白畫面 6:15–6:42
再來是可靠性:程式要能扛住錯誤,不能讓整頁掛掉。要攔截失敗、提供 fallback,而不是給使用者一片白畫面。而且正式環境一定會出 bug,所以需要能顯示 bug 發生、怎麼重現、stack trace、效能與使用者行為的工具。


推理因為上線流程再嚴謹也擋不住所有錯誤,所以作者把可靠性當成最後一項技術主題:程式要能扛住失敗,不能讓整頁掛掉——要攔截失敗並給出 fallback,而不是丟給使用者一片白畫面。而且既然正式環境一定會出 bug,就需要能顯示 bug 發生、怎麼重現、stack trace、效能與使用者行為的工具。這兩句話對應到兩個動作:先撐住畫面,再收集足夠修好它的資訊。
- CError boundary:攔截渲染錯誤切到 fallback,讓一塊壞掉不拖垮整頁→ 畫地圖
- A每個 micro-frontend 包一層 ErrorBoundary,像電路的保險絲→ 批判類比
- EgetDerivedStateFromError 切 fallback,componentDidCatch 把錯誤連 userId/route 送去監控→ 存+演練
- P替專案裡一個容易出錯的區塊加上 ErrorBoundary→ 練習
- RErrorBoundary 攔不到的錯誤類型→ 存+回想
AI 補充兩張投影片分別對應這兩個動作。第一張是 ErrorBoundary 的完整實作:`getDerivedStateFromError` 把畫面切換成 fallback、`componentDidCatch` 把錯誤送去監控服務,fallback 的文案寫的是「這一區塊載入失敗,頁面其他部分仍可使用」。最關鍵的是最後那行註解——每個 micro-frontend 各包一層 ErrorBoundary,這把可靠性直接接回了第 4 段的架構:既然各片段獨立部署,就必須獨立失敗,否則 B 團隊的一次壞部署會讓整站白畫面,獨立部署反而放大了風險。第二張是監控端:`unhandledrejection` 事件帶上 userId 與 route 一起回報,加上 PerformanceObserver 收集 LCP 與 layout-shift。這裡的重點是那行註解說的——有上下文(誰、在哪一頁)的錯誤報告才可能重現,只有一行 stack trace 的報告通常修不了。要補充的是 ErrorBoundary 的邊界:它只攔得到 React 渲染期間的錯誤,攔不到事件處理器裡、非同步回呼裡與 Promise rejection 的錯誤——後者正是第二張投影片用全域監聽補上的那一塊。兩者是互補的,不是二選一。
術語:error boundaryfallback UICore Web Vitals
13. 先問限制,再提方案 6:42–7:12
作者的收尾結論:資深工程師在面試裡最重要的不是先丟出解法,而是先問限制——業務到底要什麼、現在已經有什麼、我們實際上在解什麼問題。最後導向他的 middle to senior 前端訓練營。
推理因為前面每一段都以「這要看情況/要看專案需求」收尾,所以作者在這裡把那個反覆出現的但書提升成整支影片的結論:資深工程師在面試裡最重要的不是先提出解法,而是先問限制——業務到底需要什麼、現在已經有什麼、我們實際上在解什麼問題。這正好閉合了第 1 段的開場:如果評的是思路而不是答案,那麼最能展現思路的一步,就是先證明你會界定問題。最後他把觀眾導向自己的中階到資深前端訓練營。
AI 補充這個收尾值得多花一點力氣理解,因為它是整支影片唯一可以直接照做的行為準則。前面每一項技術都附帶一個「什麼時候不該用」——micro-frontend 在小專案是純成本、Redux 在單純的 UI 狀態是樣板、CDN 對個人化內容有風險——這些但書全都指向同一件事:沒有限制條件,就沒有正確答案。所以先問限制不是禮貌,而是取捨的前提。實務上這三個問題可以直接照用:規模與限制是什麼(多少使用者、什麼裝置與網路、團隊幾人、要多快上線)、現況是什麼(既有技術棧、既有痛點)、以及真正要解的問題是什麼(業務指標是轉換率、留存還是開發速度)。問完之後你講的每個技術選擇都有依據,而不是在報名詞——這就是第 1 段說的「只是在回答問題」與展現思路之間的全部差距。
4. 總結
作者從一個判準開始:資深面試考的不是你知不知道某個名詞,而是你會不會做取捨——所以整支影片的每一項技術都配一句「什麼時候不該用」。他沿著使用者的實際體驗往下推:打包成單一大檔會慢,於是用 code splitting 與 lazy loading 切成 chunk(切的是檔案);同樣一把刀往上抬到團隊層級就是 micro-frontend(切的是組織),拆開後緊接著要決定程式碼放在幾個 repo(monorepo 的一次改完 vs multi-repo 的團隊自主),以及執行期怎麼組回來(module federation 讓 host 從一個 CDN 網址載入對方的 remoteEntry.js,對方部署完就生效)。那個網址把主題帶到 CDN:距離就是延遲,東京打到維吉尼亞約 200ms,走當地邊緣節點命中快取約 20ms,配套問題是快取多久與怎麼失效——而答案接回了 chunk 的雜湊檔名。距離縮到最短後剩下檔案本身:tree shaking 靠 ES module 的靜態分析砍掉沒用到的匯出,所以 import 寫錯就完全失效;載入順序則靠 defer/async 避免 script 互相阻擋。程式碼跑起來後主題轉入 runtime:把資料分成 server state(真相在後端,用 React Query 管副本)、global UI state 與 local UI state,各用各的工具;如果資料是伺服器主動推的,就進入 WebSocket 的領域,斷線後要重播事件還是整份重抓,取決於推的是「事件」還是「最新狀態」。最後兩層把這一切撐住:CI 讓每個 PR 都跑過 lint、測試、build 與效能預算,feature flag 讓部署與發布脫鉤、也讓獨立部署的片段能獨立回退;error boundary 讓單一區塊的失敗不變成整頁白畫面(每個 micro-frontend 各包一層),監控則負責收集帶上下文的錯誤與 Core Web Vitals。整條鏈最後折回起點:既然每個環節都是取捨,正確的第一步就不是給答案,而是先問業務要什麼、現在有什麼、真正要解的是什麼問題。
勘誤總整理
確定錯誤/已過時見仁見智(取決於版本或情境)
| 段落 | 原話(transcript 逐字) | 說明 |
|---|---|---|
| 8. Bundle 最佳化與瀏覽器載入順序 3:37 |
「We obviously have tree shaking on the client」 | tree shaking 不發生在 client(瀏覽器),而是在建置期由打包工具做靜態分析後移除未使用的匯出——產物送到瀏覽器時已經是砍過的。這個區分很重要:正因為它是建置期的靜態分析,`require` 或整包 import 這種要到執行期才知道用了什麼的寫法才會讓它失效,也就是作者下一句自己提到的坑。 依據: webpack 官方文件 Tree Shaking 章節將其定義為 bundler 在 build 階段依 ES module 靜態結構移除 dead code |
| 7. CDN 與快取:距離就是延遲 3:26 |
「it may take just 2 milliseconds」 | 口述說命中快取只要 2 毫秒,但他自己的投影片寫的是約 20ms。2ms 對真實的網路往返(含 TLS 與最後一哩)幾乎不可能達到,即使邊緣節點就在同一座城市;20ms 才是同城命中快取的合理量級。面試時引用這個數字建議說「數百毫秒降到數十毫秒」,不要說 2ms。 依據: 同段投影片(影片 03:27)標示 Tokyo → Edge node → Cache hit ≈ 20ms |
| 9. State management:三種 state 4:42 |
「For global UI state you might want to use Redux」 | 把 Redux 當成全域 UI 狀態的預設選擇在 2026 年已偏保守。純 Redux 的樣板量大,官方本身也推薦改用 Redux Toolkit;而純 UI 狀態的新專案多半直接選 Zustand 或 Jotai(作者自己的投影片就把 Zustand 排在 Redux 前面)。Redux 仍有優勢的場景是需要嚴謹的 action 紀錄、時間旅行除錯,或團隊已有大量既有 Redux 程式碼。 依據: 同段投影片標示「Use: Zustand / Redux」,Zustand 在前;Redux 官方文件自 2021 起以 Redux Toolkit 為建議寫法 |
5. 推薦三個下一步
1. 往下挖深:把 micro-frontend 與 module federation 真的做一次
影片只用一張架構圖和一段 webpack 設定帶過,但真正的難點——共用相依的版本協商、跨片段的路由與登入狀態、遠端掛掉時的 fallback——一個都沒展開。這是整條推理鏈裡代價最高、最容易做錯的一環。
YouTube 搜尋:module federation tutorial webpack 5 micro frontend shared dependencies pitfalls micro frontend routing authentication
2. 往旁邊對照:後端的 system design 怎麼談同一組問題
快取失效、獨立部署、故障隔離這些概念都是從後端搬過來的,但後端多了資料一致性與流量控制這兩個前端沒有的維度。看過後端版本,才知道面試裡「前端 system design」的邊界劃在哪、什麼時候該說「這一段歸後端」。
YouTube 搜尋:system design interview caching strategies microservices vs monolith tradeoffs CDN cache invalidation strategies
3. 往上應用:把載入效能變成可量測、會擋合併的指標
影片提到 bundle analyzer 與 CI 裡的 lighthouse 步驟,但沒說怎麼設門檻、怎麼在真實使用者身上量。缺了這一步,前面所有效能設計都無法證明有效,也擋不住體積隨時間單向惡化。
YouTube 搜尋:core web vitals real user monitoring performance budget CI lighthouse webpack bundle analyzer reduce bundle size
- 接著看 →How Senior Frontend Engineers Think in System Design Interviews · 這站收在「先問限制再提方案」,那站整支都在示範 Clarify → Choose → Explain 怎麼做
- 接著看 →Real Frontend System Design (from a Senior Engineer) · 把這站的十三項檢查面向,實際套進一題從需求推到微前端的完整案例
- 接著看 →15 Frontend Concepts Every Senior Dev Has Mastered · 共同術語 CDN、lazy loading、Core Web Vitals;那站用關鍵渲染路徑把效能講到底
- 接著看 →Every Frontend Architecture Pattern Explained in 23 Minutes · micro-frontend 與渲染策略在那站被展開成一條「解決什麼、製造什麼」的演化鏈
- 相關 —Frontend System Design Explained w/ Senior Engineer (Microfrontends, Monorepo, MCP UI, Reactjs) · 同樣談微前端與 monorepo:那站往組織與渲染推,這站往上線流程與可靠性推
- 接著看 →The 3 Skills That Separate Architects From Senior Developers · 那站把資深工程師的檢查面向做滿,這站問何時該從交解法變成訂限制
- 相關 —Core Web Vitals Explained: LCP, INP & CLS · 同談 Core Web Vitals 與 defer:一個講機制,一個講大規模架構下的取捨
- 相關 —The ultimate guide to web performance · 同談 Core Web Vitals 與 lazy loading:一站給指標與工具,一站談大規模下的架構取捨