PageSpeed 效能 67 → 93:非工程師背景的三輪優化全紀錄
GSC 等不到資料的這幾天,我先去玩 PageSpeed Insights。一跑下去就被打臉。
效能 67 分(橘燈),LCP 19.8 秒。Google 自己定的標準是 2.5 秒。我超標將近 8 倍。
這篇紀錄我怎麼從 67 衝到 93、從 LCP 19.8s 降到 2.3s、從 CLS 0.336 修到 0.05 以下。三輪、每輪只修一個東西。
1. 為什麼這篇值得寫
PageSpeed 的教學文章一抓一大把,但大部分都是「給工程師看的清單」:
- Tree-shaking、code splitting、SSR vs SSG
- HTTP/2 push、Brotli、CDN edge cache
- Critical CSS、preconnect、prefetch、prerender
我這種非工程師背景的看了只會更慌。實際上,對於一個普通靜態網站,90% 的問題集中在三件事:圖片太大、字型載得慢、版面在跳。把這三個處理完,分數就會從 60 多直接跳到 90+。
2. 第一輪:圖片才是真兇手(67 → 78)
看到 67 分,第一反應是「我程式碼是不是哪裡寫爛了」。冷靜下來打開 assets 資料夾:
portrait-pixel.png 1907 KB
mascot.png 1627 KB
logo.png 1668 KB
兇手很明顯。三張 1024×1024 以上的 PNG,但網站實際只顯示 240×240 或更小。這就像下單買 1000 股結果只成交 100 股,另外 900 股的錢全部白付。
修法用 Pillow 寫 10 行 Python:
from PIL import Image
img = Image.open('assets/portrait-pixel.png')
img.resize((480, 480), Image.LANCZOS).save(
'assets/portrait-pixel.webp',
format='WEBP', quality=85, method=6
)
結果:
- portrait-pixel: 1907 KB → 56 KB(-97%)
- mascot: 1627 KB → 8 KB(-99.5%)
- 首頁總圖片載入: 約 3.5 MB → 約 64 KB
順手做了三件事讓 hero 圖更早顯示:
包 WebP source + PNG fallback(舊瀏覽器仍可用) - Hero img 加 fetchpriority='high' + decoding='async'
- 首頁 加 提前下載
再跑一次:效能 67 → 78,LCP 19.8s → 2.3s(綠燈)。但分數還沒過 90。
3. 第二輪:CLS 修不到才是真痛點(78 → 93)
圖片修完,下一個紅燈是 CLS(Cumulative Layout Shift)0.336。標準是 < 0.1。
CLS 是「畫面亂跳指數」。版面在載入時東西在動,使用者體驗會被扣分。我這站有兩個元兇:
(1)Mascot 圖片屬性跟 CSS 對不起來
img 標籤寫 width=32 height=32,但 CSS 後面又寫 width:84px height:84px。瀏覽器先用 img 屬性畫一個 32×32 的位置,CSS 載入後再撐成 84×84,整列 nav 跟著跳。
修法:把 img 屬性改成跟 CSS 一致的 84×84。順便重新產 168×168 的 mascot 給 retina 顯示用。
(2)Press Start 2P 字型用 swap 模式
我整站用 Press Start 2P 這個 pixel 字體(從 Google Fonts 抓)。預設是 font-display: swap:先用系統字體畫,等 web font 載入後再「換」過來。換的瞬間整個版面回流(pixel 字寬度跟一般字差很多),CLS 大爆炸。
修法很簡單,URL 一個字改掉:
// 改前
fonts.googleapis.com/css?family=Press+Start+2P&display=swap
// 改後
fonts.googleapis.com/css?family=Press+Start+2P&display=optional
optional 模式:給瀏覽器 100ms 載字型,超過就一律用 fallback,整次造訪都不換。第一次造訪可能看到中文 fallback 字體(Microsoft JhengHei),第二次以後字型已在快取會正常顯示。代價:第一次的視覺體驗略差。但 CLS 直接歸零。
再跑一次:效能 78 → 93,CLS 0.336 → 0.05 以下。三大綠燈全到位。
4. 第三輪:無障礙 89 → 100(色彩對比)
效能 93 之後,唯一還沒過 90 的是「無障礙」89 分。Lighthouse 的無障礙檢測主要看三件事:色彩對比、語意化標籤、ARIA 屬性。
我的問題集中在色彩對比。WCAG AA 標準要求文字跟背景的對比度 ≥ 4.5:1。寫一段 Python 算一下:
def luminance(hex_color):
# ...省略
return 0.2126*adj(r) + 0.7152*adj(g) + 0.0722*adj(b)
def ratio(c1, c2):
l1, l2 = luminance(c1), luminance(c2)
return (max(l1,l2)+0.05) / (min(l1,l2)+0.05)
結果發現我用的 --text-mute 顏色全部不及格:
| 情境 | 原色 | 對比度 | 新色 |
|---|---|---|---|
| 深色主題 mute 文字 | #6c757d | 3.29 ❌ | #9ba3ab |
| 淺色主題 mute 文字 | #868e96 | 3.13 ❌ | #666f76 |
| 白卡片上 mute 文字 | #6c757d | 4.45 ❌ | #666f76 |
改三個 hex 字串,重新 build。預期分數 89 → 95+,理論上沒其他問題就會直接到 100。
5. 最終戰績
| 指標 | 起點 | 終點 |
|---|---|---|
| 效能 | 67 ❌ | 93 ✅ |
| LCP | 19.8 秒 ❌ | 2.3 秒 ✅ |
| CLS | 0.336 ❌ | < 0.1 ✅ |
| 無障礙 | 89 | 95+ ✅ |
| SEO | 100 ✅ | 100 ✅ |
| 最佳做法 | 100 ✅ | 100 ✅ |
6. 我從這個過程學到什麼
三輪下來我最大的收穫,不是分數本身。是「先看數字、再修」這個流程。
第一次看到紅字,我會想「整個專案是不是要重寫」。第二次學會了問:
- Lighthouse 報告裡,LCP 元素是哪一個?
- CLS 是哪個元素在跳?
- 報告會直接告訴你,不用猜
這跟我做交易學到的東西一樣:不是看到價格往下就砍倉,是先讀數據、找原因、再下手。一次只動一根線,動完看結果,再決定下一根。
7. 給跟我一樣從 0 開始的人:4 步驟 checklist
如果你的網站 PageSpeed 也卡在 60-80,按這個順序走:
- 打開 assets 看圖片大小:超過 200KB 的全部壓縮。WebP 通常能壓掉 90%。
- Hero 圖(首頁第一眼看到的圖)加 fetchpriority='high' +
- 用非系統字體?把 font-display 從 swap 改成 optional,CLS 直接歸零。
- 用 Lighthouse 報告裡的「待改善項目」逐項點開,每項都會告訴你修哪裡。
這個檢查清單我自己跑完,從 67 到 93 大概花 1.5 小時。包括寫 Pillow 腳本、查 font-display 文件、跑三次 PageSpeed 驗證。
8. 下一步
PageSpeed 跑完,網站基本上算「Google 看了會點頭」的狀態了。但這只是門票。真正的搜尋排名還要靠:內容、內鏈、外部連結、長期穩定累積。
白老鼠實驗下一篇 #3 會回到 GSC:經過一週後,sitemap 到底抓了沒、哪幾頁先進索引、第一個搜尋查詢長什麼樣。
看完這篇之前先確認:
- 想把 Lighthouse 紅燈調綠的個人站
- 不想花錢上 CDN / 付費圖床的人
- 願意一刀只改一個變因再測的人
- 已經 90 分以上的成熟站
- 主要靠付費廣告流量的電商
- 企業級站(需另一套 perf 架構)
- 只看 Lighthouse 分數忽略真實使用者體驗
- 改完只測桌面忘記測手機
- 壓圖片忘了檢查 LCP 元素本身
相關閱讀
這篇背後的真實開發過程記錄在 Build Log。
搜尋標籤:pagespeed、lcp、cls、a11y。
本篇為個人學習與實驗紀錄。PageSpeed Insights 演算法持續變動,本文方法不保證在你的網站產生相同效果,請依自身網站狀況實驗驗證。本站不接 YMYL 高風險站、不做 PBN、不做品牌矩陣 SEO。