PageSpeed mobile 79 → 89:7 個 critical path 動作 + 3 個踩坑
文 / Coolkid發布:2026-05-19 · 最後更新:2026-08-07閱讀約 8 分鐘

摘要
PageSpeed 分數每次跑都不一樣正常嗎?跑分本來就會上下跳 5 到 10 分,別看單次就下大決定。Lab 結構大改後 mobile 掉到 79,我用 7 個關鍵路徑動作把它拉回 86 到 89,包含拿掉阻塞渲染的 CSS、預先載入字體、拍掉打字機動畫、訪客的 JS 少載 23KB,並且說明為什麼停在 89 是合理的停損點。
#10 把全站結構對齊 AIO 5 原則做完,PageSpeed 還停在 80 多。
這篇紀錄接下來 24 小時,把 mobile 從 79 拉回 86 到 89 穩定區的 7 個動作,還有 3 個踩到的坑。
誠實揭露一下。#10 結尾預告要做三件事,抽關鍵 CSS 內嵌、自製 nes.min.css 子集、Google Fonts 的 media 小技巧。
實際做完的是不一樣的 7 個動作。兩個經過風險評估後跳過,一個解法被換掉。為什麼轉向,也一起寫進來。
讀完你會知道,7 個動作的執行排序、3 個常見的效能地雷怎麼避,還有為什麼追到 PageSpeed 95 分以上,對個人站是走火入魔。
1. 為什麼 PageSpeed 跑分會跳?三跳教訓 (89 → 85 → 79)
動工之前先講一個很容易誤判的現象。PageSpeed 的跑分有正負 5 到 10 分的波動。
我那天連跑三次,拿到 89、85、79 三個分數。同一份程式碼,同一個網路,三分鐘內三跳。
原因是 Lighthouse 的 mobile 模式,跑在一個模擬的「中階手機加慢速 4G」環境裡,模擬本身就會抖。
如果只看單次跑分,很容易做出兩種錯誤判斷:
- 看到 89 就以為「優化夠了」 — 其實隨便再跑一次可能掉到 79
- 看到 79 就以為「上次的改動失敗」 — 結果是 variance 不是 regression
正確做法是每次改動後跑 3 到 5 次,取中位數。
本文以下所有的改前改後分數,都是中位數,不是單次。
2. 怎麼拿掉 render-blocking CSS?(為什麼放棄「critical CSS inline」)
Lab 用了 nes.min.css,288KB 的怪物,但視覺風格整個依賴它。另外還有 Google Fonts。
這兩個都是阻塞渲染的 CSS,瀏覽器要等它們下載完,才會開始畫畫面。
原本 #10 結尾預告要做「抽關鍵 CSS 內嵌」,把首屏要用的 CSS 抽 1 到 2KB 內嵌進 HTML。另外還要自製一份 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 有 FOUC 風險 — 抽錯一條樣式首屏會閃
- 自製 nes-minimal.css 要 2-3h 維護成本 + 升級 nes.css 時要重抽
- media=print trick 已收 80% 收益 — 剩下 20% 風險太高 ROI 不划算
效能優化最大的迷思,是「該做的全部做完」。
實際上應該按投報率排序,做到邊際遞減就停手。
3. 動作 3-4:字體 preload + woff2(只 preload 真在 LCP 路徑的資源)
字體跟 LCP 直接相關,因為 hero 標題用了自訂字體。
字體沒下載完,LCP 就不算發生。瀏覽器要嘛等字體,要嘛先用備用字體畫再換過去,而後者會觸發 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
關鍵教訓是,預先載入不是越多越好。
我中間試過把 mascot.webp 也預先載入,結果跑分反而掉。因為它跟字體搶頻寬,而 mascot 根本不在 LCP 元素的路徑上。
後來把 mascot 的預載移除,跑分就回升了。
4. 動作 5-6:拿掉 LCP 拖累 (typewriter + body transition)
兩個我自己加的「視覺優化」,結果都在拖垮 LCP:
- 動作 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 元素上的動畫,不管是淡入、進場位移,還是打字機逐字打,都會直接把 LCP 拖慢 1.5 到 2 秒。
視覺再漂亮也不該加。
5. 動作 7:visitor-overlay.js 3.5KB 取代 admin 27KB(訪客 JS payload -23KB)
Lab 有一個站內編輯功能,管理者點鉛筆圖示,就能在公開頁面上直接改內容。
原本所有訪客都要載那支 27KB 的編輯腳本,雖然訪客根本看不到編輯按鈕。
拆分:
- visitor-overlay.js (3.5KB) — 所有訪客載 只處理顯示邏輯
- edit-overlay.js (27KB) — 只 admin (有 token 或
?edit=1) 才 dynamic import 載入
對訪客來說,JS 少載了 23KB。這對 TBT 跟 FCP 都有直接幫助,因為 JS 的解析跟執行會卡住主執行緒。
這也是「為使用者優化,而不是為自己優化」的具體做法。
我自己載得多沒差,訪客省得越多越好。
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 分重要幾倍的是「下一篇文章寫了沒」 — 內容才是 SEO / GEO 的本體
個人站的合理停損點是,mobile 87 分以上,TBT 綠,CLS 綠,LCP 在 3.5 秒以下。
剩下的時間拿去寫文章、做 GEO 結構、跟讀者互動。效能是基礎建設,不是品牌。
AIO 結構(#10)加上關鍵路徑效能(#11)兩戰打完,Lab 的結構面跟速度面都對齊了 GEO 時代。結果剛打開 GA4,就跳出第一筆來自 ChatGPT 搜尋的訪客。完整的倒推方法寫在 #12 GA4 跳出第一筆 ChatGPT referral!倒推 AI 引用來源的 4 個方法,GA4 自訂報表、GSC 反向查、Bing Webmaster、反向 ChatGPT 搜尋驗證,總共四種。
名詞解釋
- 最大內容繪製(LCP, Largest Contentful Paint)
- 頁面上「最大那塊內容」(通常是首圖或大標題)出現所需的秒數。Google 標準 ≤ 2.5 秒。
- CSS
- 網頁的造型語言,管顏色、字體、排版跟動畫。
- PageSpeed Insights
- Google 提供的免費網站速度體檢工具,輸入網址就給 0-100 分跟逐項改善建議。
- 預載(preload)
- 在網頁開頭先宣告「這個檔案很重要,先下載」。把首圖設成預載,使用者第一眼的畫面就會更早出現。
- 阻塞渲染(render-blocking)
- 瀏覽器「必須先下載完這個檔,才能開始畫畫面」的資源。這類資源越多,使用者盯著白畫面的時間越久。
- 關鍵路徑(critical path)
- 從輸入網址到第一眼畫面出現,瀏覽器必須走完的一串步驟。優化它等於直接縮短「白畫面時間」。
- 累積版面位移(CLS, Cumulative Layout Shift)
- 畫面亂跳指數:載入過程版面移動越多、分數越高。標準 < 0.1,常見元兇是圖片沒寫尺寸跟字型替換。
- Lighthouse
- PageSpeed 背後的檢測引擎,Chrome 瀏覽器也內建。報告會逐項列出「哪裡慢、該修什麼」,照著修就好,不用猜。
- WebP
- Google 推的圖片格式,同樣畫質下檔案比 PNG / JPG 小很多,常見能省 90% 以上。
- 備用字體(fallback font)
- 指定字型載不到時,瀏覽器改用的替代字體,例如系統內建的微軟正黑體。
看完這篇之前先確認:
- 想優化 PageSpeed 不知從哪開始
- 用了 nes.css / Bootstrap 重 CSS 站
- 想理解跑分為何會 ±5-10 跳
- 純動態站(Next.js 已內建 critical CSS)
- 已經穩定 95+ 的人
- 把 PageSpeed = 真實體驗的人
相關閱讀
- 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。
