📌 30 秒摘要(Layer 3)
一個反直覺卻關鍵的發現:網頁 80–90% 的載入時間花在前端(下載 JS/CSS/圖片/資源),而非後端產生 HTML。所以想讓網站變快,優化前端的投報率遠高於優化後端。本書把它濃縮成一組可照做的規則——核心是減少 HTTP 請求數、善用快取與 CDN、壓縮與資源擺放位置。雖成書於較早年代,但「先量測前端、減少往返、善用快取」的原則至今通用。
🗺 心智圖(Canvas)
高性能網站建設指南
Link to original 高性能網站建設指南
High Performance Web Sites — Steve Souders
🥇 黃金法則:前端優先
🎯 什麼情境該想到我
當網站/頁面「載入很慢、首屏遲遲不出來」,你想知道從哪裡下手優化時。
⚙️ 怎麼用
黃金法則:多數載入時間在前端 → 先優化前端(比調後端 CP 值高)。核心是減少往返次數。
- 減少 HTTP 請求:合併 JS/CSS、用 CSS sprites、行內小圖(越少往返越快)。
- JS 放底部、CSS 放頂部:避免腳本阻塞頁面渲染。
- 壓縮傳輸:開 Gzip/Brotli,縮小 JS/CSS/圖片。
- 用 CDN:靜態資源就近提供,降低延遲。
- 先量測再優化:用瀏覽器開發者工具/效能瀑布圖找真正的瓶頸資源。
★ 書中的 14 條規則,今天還剩多少
《高性能網站建設指南》(Steve Souders,2007)的 14 條規則,在 HTTP/2、HTTP/3 與現代瀏覽器之下重新評估:
# 規則 今天 理由 1 減少 HTTP 請求數 ⚠️ 部分過時 HTTP/1.1 下瀏覽器對同一主機只開少數幾條連線,請求多就排隊。HTTP/2 在一條連線上多工,合併檔案、CSS sprites 的收益大幅降低;但往返次數仍然重要 2 使用 CDN ✅ 仍然有效 物理距離決定延遲 3 加上 Expires/快取標頭 ✅ 仍然有效 改用 Cache-Control(見 工具-快取與資源優化)4 Gzip 壓縮 ✅ 仍然有效 現在多用 Brotli,壓縮率更好 5 CSS 放頂部 ✅ 仍然有效 避免無樣式內容閃爍 6 JS 放底部 ✅ 概念有效,做法改變 改用 <script defer>或type="module"(預設延遲執行),不必真的放在底部7 避免 CSS 運算式 ❌ 已消失 那是舊版 IE 的功能 8 JS 與 CSS 放外部檔案 ✅ 大致有效 例外:首屏的關鍵 CSS 常內嵌,省一個往返 9 減少 DNS 查詢 ✅ 仍然有效 也可用 <link rel="preconnect">提前連線10 最小化 JS ✅ 仍然有效 建置工具預設就會做 11 避免重新導向 ✅ 仍然有效 每次重導向多一個完整往返 12 移除重複的腳本 ✅ 仍然有效 現代打包工具大多會處理 13 設定 ETag ✅ 有效,但要設對 多台伺服器的 ETag 若包含伺服器特有的資訊,會導致驗證永遠失敗 14 讓 Ajax 可被快取 ✅ 仍然有效 現在是 fetch回應的快取標頭不變的核心:書的黃金法則「大部分的載入時間花在前端」和「減少往返」仍然成立。變的是達成手段。
★ 今天的量尺:Core Web Vitals
Google 定義的三個核心指標與「良好」門檻(依 web.dev,2026-09-25 查證):
指標 量什麼 良好門檻 LCP(Largest Contentful Paint) 載入:最大的內容元素多久出現 ≤ 2.5 秒 INP(Interaction to Next Paint) 互動:使用者操作到畫面更新多久 ≤ 200 毫秒 CLS(Cumulative Layout Shift) 視覺穩定:版面跳動的程度 ≤ 0.1
- 評估方式是看第 75 百分位的頁面載入,分行動裝置與桌機——不是平均,也不是你自己的高速電腦。
- INP 取代了早期的 FID(First Input Delay)。
對應到這張卡的規則:LCP 主要受規則 1–6、9、11 影響(資源下載與阻塞);INP 受 JavaScript 執行量影響(書出版時還不是主要問題);CLS 則是新的關注點——圖片與廣告要預留空間(設
width/height或aspect-ratio)。★ 量測的工具鏈
工具 量什麼 什麼時候用 瀏覽器開發者工具的 Network 瀑布圖 每個資源的時間、阻塞、順序 找出是哪個資源拖慢 Lighthouse 實驗室環境的模擬分數 開發時快速檢查 真實使用者監測(RUM),例如 web-vitals函式庫真實使用者的 LCP/INP/CLS 上線後——實驗室分數好不代表真實使用者體驗好 🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
書中細節(如某些 HTTP/1 技巧)在 HTTP/2 後有變;但「減少往返、量測導向」的原則不變。
SPA 的陷阱:單頁應用把大部分工作搬到 JavaScript,首屏要下載、解析、執行一大包 JS 才能顯示內容。書的規則管不到這個——需要伺服器端渲染、程式碼分割、減少 JS 體積。
🔗 相關工具
- 工具-快取與資源優化 —— 規則裡最高槓桿的那一項,重複造訪的速度幾乎由它決定
- 工具-透明多級分流 —— 往後端延伸:前端規則管瀏覽器到 CDN,這張管 CDN 之後到服務節點
- 工具-簡單清楚至上 —— 提醒優化的邊界:先量測再動手,別為了省幾十毫秒寫出沒人看得懂的程式
- 工具-LLM推論最佳化 —— 同一套紀律換到 LLM 場域:先量出瓶頸在哪一段,再決定套哪個手法
80–90% 的使用者等待時間發生在前端 優化前端的投報率遠高於優化後端
先量測再優化 用開發者工具/效能瀑布圖找出真正的瓶頸資源
📉 減少 HTTP 請求
合併檔案 多支 JS/CSS 合併成一支,減少往返次數
CSS Sprites 把多張小圖併成一張,用背景位移取用
行內小資源 極小的圖/樣式直接 inline,省掉一次請求
🗄 快取與 CDN
🎯 什麼情境該想到我
當使用者「重複造訪網站卻還是慢」、資源每次都重新下載時。
⚙️ 怎麼用
- 設快取標頭:對靜態資源加長
Cache-Control/Expires,讓瀏覽器重複造訪直接用本地副本。- 內容雜湊檔名(cache busting):檔名帶 hash(如
app.9f3c.js),內容一改檔名就變 → 可放心設「永久快取」,更新自動失效。(本站 Quartz 就是這樣。)- 善用多層快取:瀏覽器 → CDN → 反向代理 → 應用快取,越外層命中越省。
- 壓縮與合適格式:圖片用適當格式/尺寸,別傳過大資源。
★
Cache-Control的五個關鍵指令
指令 意思 常見誤解 max-age=NN 秒內直接用快取,不問伺服器 no-cache可以存,但每次使用前都要向伺服器驗證 常被誤以為是「不要快取」 no-store完全不要存 真正的「不要快取」;用在含個資的回應 immutable在 max-age 期間內容絕不會變,連重新整理都不用驗證 只能搭配內容雜湊檔名使用 private/public只能存在瀏覽器/CDN 等共用快取也可以存 含使用者資料的回應一定要 private★ 兩種資源、兩種策略
資源 例子 標頭 更新怎麼生效 帶雜湊的靜態檔 app.9f3c2a.js、style.a81b.cssCache-Control: public, max-age=31536000, immutable內容改變 → 檔名改變 → 等於新檔案 入口 HTML(和其他不能改名的) index.htmlCache-Control: no-cache+ETag每次驗證;沒變就回 304 Not Modified(不傳內容) 這個組合的邏輯:HTML 每次都驗證(很小,304 只要一個往返),HTML 裡引用的帶雜湊檔名則永久快取。部署新版本時,新的 HTML 指向新的檔名,舊檔案自然不再被使用。不需要手動清快取。
★ 驗證的機制:ETag 與 304
- 第一次請求:伺服器回
200+ETag: "abc123"。- 之後的驗證:瀏覽器送
If-None-Match: "abc123"。- 內容沒變:伺服器回
304 Not Modified,沒有 body → 省下傳輸量,但仍然有一個往返。- 內容變了:回
200+ 新內容 + 新 ETag。所以
no-cache+ ETag 省的是頻寬,max-age省的是往返。行動網路上一個往返可能要幾百毫秒,這就是為什麼帶雜湊的資源要用max-age+immutable。★ Go 的做法
// 帶雜湊的靜態檔:永久快取 fs := http.FileServerFS(assets) // Go 1.22+;assets 是 embed.FS 或 os.DirFS mux.Handle("GET /assets/", http.StripPrefix("/assets/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") fs.ServeHTTP(w, r) })))
http.FileServer會自動處理If-Modified-Since/Last-Modified,並在內容沒變時回 304。- 但用
embed.FS嵌入檔案時,檔案的修改時間是零值,Last-Modified機制不會生效 → 靠檔名雜湊 +immutable,或自己計算 ETag。★ 多層快取的失效順序
瀏覽器 → CDN → 反向代理 → 應用程式快取 → 資料庫。失效要從內往外:先更新資料庫,再清應用程式快取,最後清 CDN。反過來做,外層清完後可能馬上又從內層拿到舊資料再快取一次。
stale-while-revalidate=N:過期後的 N 秒內,先回傳舊的快取給使用者,同時在背景重新驗證——使用者不用等,資料最多舊 N 秒。適合「稍微舊一點沒關係」的內容。🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
快取最難的是「失效」;用內容雜湊檔名可一併解掉「更新了卻還吃到舊檔」的問題。
CDN 快取了錯誤回應:後端短暫出錯回了 500 或空白頁,如果沒有正確的
Cache-Control,CDN 可能快取它並發給所有使用者。錯誤回應要明確設no-store。
Vary標頭:同一個網址會依照Accept-Encoding、Accept-Language回不同內容時,要設Vary,否則 CDN 可能把 gzip 版本發給不支援的客戶端,或把中文版發給英文使用者。🔗 相關工具
- 工具-前端效能優化規則 —— 快取只是其中一條規則;跳過去拿完整清單,判斷該先動快取還是先減請求數
- 工具-透明多級分流 —— 多層快取的上游架構:瀏覽器 → CDN → 反向代理這條路徑本身該怎麼擺
加 Expires/Cache-Control 標頭 讓重複造訪直接用本地副本,不再重抓
內容雜湊檔名(cache busting)
app.9f3c.js:可放心永久快取,更新自動失效使用 CDN 靜態資源就近提供,降低網路延遲
📦 壓縮與資源擺放位置
Gzip 壓縮傳輸內容 文字類資源壓縮後體積大幅下降
CSS 放頁面頂部 讓瀏覽器盡早開始漸進渲染,避免白畫面
JS 放頁面底部 腳本下載與執行會阻塞渲染,往後擺
🧰 這本書給我的工具
- 工具-前端效能優化規則 — 網站/頁面載入太慢時
- 工具-快取與資源優化 — 想讓重複造訪與資源載入更快時
✨ 關鍵重點(Layer 1–2)
- 黃金法則:多數載入時間在前端 → 優化前端 CP 值最高。
- 減少 HTTP 請求:合併檔案、CSS sprites、行內小資源。
- 用 CDN 讓靜態資源就近提供。
- 加 Expires/快取標頭:讓瀏覽器重複造訪不再重抓。
- Gzip 壓縮傳輸內容;CSS 放頂部、JS 放底部,避免阻塞渲染。
💬 金句原文(Layer 0)
- 「80% 到 90% 的使用者等待時間,發生在前端。」
🔗 相關
- 入口流量分層見 工具-透明多級分流(CDN/快取是其中一環)