PageSpeed mobile 79 → 89:7 個 critical path 動作 + 3 個踩坑
#10 把全站結構對齊 AIO 5 原則做完 PageSpeed 還停在 80 多。這篇紀錄接下來 24 小時把 mobile 從 79 拉回 86-89 穩定區的 7 個動作 + 3 個踩坑。
誠實揭露:#10 結尾預告要做「critical CSS inline 子集 / nes.min.css 自製子集 / Google Fonts media trick」三件事。實際做完的是不一樣的 7 個動作 — 兩個被風險評估後跳過 一個解法被換掉。為什麼轉向也寫進來。
讀完你會知道:7 個動作的執行排序、3 個 perf 常見踩坑怎麼避、為什麼追到 PageSpeed 95+ 對個人站是走火入魔。
1. 為什麼 PageSpeed 跑分會跳?三跳教訓 (89 → 85 → 79)
動工前先講一個容易誤判的現象:PageSpeed 跑分有 ±5-10 分 variance。我那天連跑三次拿到 89 → 85 → 79 三個分數 — 同一份 code 同一個網路 三分鐘內三跳。
原因是 Lighthouse mobile 模式跑在模擬的「中階手機 + 慢速 4G」環境 模擬本身就會抖動。如果只看單次跑分容易做出兩種錯誤判斷:
- 看到 89 就以為「優化夠了」 — 其實隨便再跑一次可能掉到 79
- 看到 79 就以為「上次的改動失敗」 — 結果是 variance 不是 regression
正確做法:每次改動後跑 3-5 次取中位數。本文以下所有「改前 / 改後」分數都是中位數 不是單次。
2. 動作 1-2:拿掉 render-blocking CSS (為什麼放棄「critical CSS inline」)
Lab 用了 nes.min.css(288KB 怪物 — 但視覺風格依賴它)+ Google Fonts。兩者都是 render-blocking CSS — 瀏覽器要等它們下載完才開始畫。
原本 #10 結尾預告要做「critical CSS inline 子集」(把首屏要的 CSS 抽 1-2KB inline 到 HTML)+「nes.min.css 自製子集」(手寫 nes-minimal.css 砍到 15-30KB)。實際只做了下面兩個更便宜的動作:
- 動作 1:nes.min.css 改
media="print" onload trick— 瀏覽器以為這是列印用 CSS 不擋 critical path 載入後 JS 改 media 回 all 套用。比 preload 還低 priority 收益 80% - 動作 2:拿掉 Google Fonts
<link>CSS — site.css 內已有 @font-face 直接用本機字體 砍 1 個 render-blocking request
為什麼放棄 critical CSS inline + 自製子集:
- critical CSS inline 有 FOUC 風險 — 抽錯一條樣式首屏會閃
- 自製 nes-minimal.css 要 2-3h 維護成本 + 升級 nes.css 時要重抽
- media=print trick 已收 80% 收益 — 剩下 20% 風險太高 ROI 不划算
perf 優化最大的迷思是「該做的全部做完」 — 實際上應該排序 ROI 做到邊際遞減就停。
3. 動作 3-4:字體 preload + woff2(只 preload 真在 LCP 路徑的資源)
字體跟 LCP 直接相關 — 因為 hero 標題用了自訂字體 字體沒下載完 LCP 不算發生(瀏覽器要嘛等字體 要嘛先用 fallback 字體畫再換 — 後者觸發 CLS)。
- 動作 3:hero 字體 woff2 加
<link rel="preload" as="font" crossorigin>— 比 CSS @font-face 提前發 fetch 大概省 200-400ms - 動作 4:site.css 內 @font-face 直接指 woff2 本機檔 取代外部 Google Fonts CSS — 少 1 跳 DNS + 1 個 render-blocking
關鍵教訓:preload 不是越多越好。我中間試過把 mascot.webp 也 preload — 結果跑分反而掉 因為它跟字體搶頻寬 而 mascot 根本不在 LCP 元素路徑上。後來移除 mascot preload 跑分回升。
4. 動作 5-6:拿掉 LCP 拖累 (typewriter + body transition)
兩個自己加的「視覺優化」結果都是 LCP killer:
- 動作 5:整個刪 typewriter.js + 所有 .tw-char / .typewriter CSS — typewriter 進場動畫把首頁 PageSpeed 從 93 拖到 83(完整 debug 在 PageSpeed 93 → 83 又修回:typewriter LCP killer)
- 動作 6:body { transition } 改
.theme-transitioningJS toggle — 原本首屏載入就觸發 transition 拖慢 paint 改成只在切換 light/dark 時才加 class
通則:任何在 LCP 元素上做 opacity 0→1 fade in / transform 進場 / typewriter 逐字打的動畫 都會直接把 LCP 拖 1.5-2 秒。視覺再漂亮也不該加。
5. 動作 7:visitor-overlay.js 3.5KB 取代 admin 27KB(訪客 JS payload -23KB)
Lab 有 inline-edit 功能 — admin 點 ✏️ 圖示可以在公開頁面上直接編輯內容。原本所有訪客都載 edit-overlay.js (27KB)雖然訪客根本看不到編輯按鈕。
拆分:
- visitor-overlay.js (3.5KB) — 所有訪客載 只處理顯示邏輯
- edit-overlay.js (27KB) — 只 admin (有 token 或
?edit=1) 才 dynamic import 載入
對訪客 -23 KB JS payload。對 TBT / FCP 都有直接幫助 因為 JS parse + execute 是 main thread blocking。
這也是「為使用者而不是為自己優化」的具體實作 — admin (我) 載得多沒差 訪客省得越多越好。
6. 3 個反面教訓(踩過的坑都公開)
| 想做 | 結果 | 教訓 |
|---|---|---|
| preload mascot.webp | 跑分掉 3-5 分 | preload 只給真在 LCP 路徑上的資源 |
| 加 typewriter 進場 | 93 → 83 (-10 分) | JS 動畫不該碰 LCP 元素 |
| 看單次跑分判斷 | 89→85→79 誤判 | 每次改動跑 3-5 次取中位數 |
踩坑跟動作一樣重要 — 動作告訴你「怎麼做對」 踩坑告訴你「別做什麼」。後者通常比前者省更多時間。
7. 對照分數 + 為什麼停在 86-89 是合理停損點
| 指標 | 改前(中位數) | 改後 | 狀態 |
|---|---|---|---|
| Mobile 總分 | 79-83 | 86-89 | +6-10 分 |
| LCP | ~4s | 2.5-3.5s | 🟠 還橘 |
| FCP | ~2.5s | 1.3-2.0s | 🟠 還橘 |
| TBT | ~150ms | ~40ms | 🟢 綠 |
| CLS | ~0.05 | 0-0.04 | 🟢 綠 |
TBT 跟 CLS 都過綠線 LCP 跟 FCP 還在橘。但我選擇停在這 不繼續追 95+。理由:
- 邊際收益遞減 — 79 → 89 花 24 小時 89 → 95 至少要再花 2-3 天
- 剩下優化(zoom→rem 全站重算 / critical CSS inline 子集)視覺風險高 改壞跑版的 cost 比 5 分跑分高
- PageSpeed ≠ 真實體驗 — 真實用戶用的是 4G+ / Wifi 不是 Lighthouse 模擬的慢速 4G
- 比追 95 分重要 100 倍的是「下一篇文章寫了沒」 — 內容才是 SEO / GEO 的本體
個人站合理停損點:Mobile 87+ TBT 綠 CLS 綠 LCP 在 3.5s 以下。剩下時間拿去寫文章 / 做 GEO 結構 / 跟讀者互動。perf 是基礎建設 不是品牌。
AIO 結構(#10)+ critical path perf(#11)兩戰打完 Lab 結構面 + 速度面都對齊 GEO 時代。下一站:GSC 觀察 1-2 個月 看 AI 引用 referrer 數據 / 哪些頁面開始被 AI 摘要引用。資料夠了再寫 #12。
看完這篇之前先確認:
- 想優化 PageSpeed 不知從哪開始
- 用了 nes.css / Bootstrap 重 CSS 站
- 想理解跑分為何會 ±5-10 跳
- 純動態站(Next.js 已內建 critical CSS)
- 已經穩定 95+ 的人
- 把 PageSpeed = 真實體驗的人
- 看單次跑分就下大決定
- preload 越多越好的迷思
- JS 動畫加在 LCP hero 元素上
相關閱讀
- #10 對齊 Google AIO 5 原則 全站結構大改造:7 個動作 24 小時實測
- PageSpeed 93 → 83 又修回:typewriter 動畫遇到 LCP 元素的真實 debug
- #2 PageSpeed 效能 67 → 93:三輪優化全紀錄(早期 perf 戰役)
- #9 全站 19 篇嵌入「適合 / 不適合 / 最常踩」block:GEO 第二動 Quotable Blocks
這篇背後的真實開發過程記錄在 Build Log。
搜尋標籤:perf、pagespeed、critical-path、lcp、build-in-public。
本篇為個人學習與實驗紀錄。PageSpeed 跑分受網路條件 / Lighthouse 版本 / 模擬硬體影響有 variance 本文數據為 Lab 實測中位數。不同站體質不同(框架 / CSS 大小 / 字體策略)請依自身狀況實驗驗證。本站不接 YMYL 高風險站、不做 PBN、不做品牌矩陣 SEO。