只用六行 HTML,肉眼可見地加速任意應用的加載
我曾經也自信地“發貨”:應用一打開就是骨架屏 + 金句,而真正的頁面在后臺做仰臥起坐。 用戶喜歡那句雞湯,但討厭等待。
后來我試了一個小到有點冒犯的 HTML 片段,對瀏覽器說: “他們大概率要點這個,先去把下一頁準備好。”
結果?旋轉菊花瞬間像翻蓋手機——復古,但在公共場合拿不出手。
這就是那招。
這 6 行
<script type="speculationrules">
{
"prerender": [{ "source": "document", "eagerness": "moderate" }]
}
</script>就這幾行,讓你的應用看起來像會讀心術: 瀏覽器會在用戶尚未確定點擊前,提前準備最可能的下一跳頁面; 當他們真的點擊時——幾乎即時呈現。
這不是“神油”,而是現代 Chromium 系瀏覽器內建能力,采用聲明式規范,讓瀏覽器按自身策略最優調度。
Speculation Rules API 到底是什么?
一句話:用聲明式規則告訴瀏覽器,“哪些潛在的導航值得提前準備”,分兩檔:
- Prefetch:先把目標頁的 HTML 下下來,放進緩存。 不執行 JS、不布局、沒副作用,便宜且安全。把它當作先擺好餐具。
- Prerender:在隱藏的上下文里把目標頁整頁加載并運行(腳本、數據、樣式都走一遍)。 一旦發生導航,直接激活那一頁,瞬切。更強,但更吃資源。這就像整餐先做好、罩上保溫蓋。
你不寫預測代碼,只寫“規則”。 哪些 URL、在什么時機值得準備,瀏覽器自己完成“怎么做”。
為什么要聲明式?
瀏覽器掌握的上下文比你的頁面多得多:網絡質量、設備溫度、標簽可見性、電量、歷史行為…… 把“決策權”交給引擎,調度往往比你凌晨 2 點寫的 onmouseover startFetch 要聰明得多。
別混淆:Prefetch vs. Prerender
圖片
- Prefetch:下載目標頁 HTML 主體,不執行。 省資源、風險低,大范圍鋪開時的首選。
- Prerender:隱身加載 + 運行,導航時零等待。 適合高置信度路徑(如“商品 → 結賬”、“下一篇”、“已登錄的儀表盤 Tab”)。
經驗法則:廣度用 Prefetch,關鍵路徑用 Prerender。
“eagerness” 準星(多早開始準備)
conservative—— 等到強意圖(例如mousedown/touchstart)。風險最低,收益也最低。moderate—— 對中等信號(例如 hover)響應。默認夠用。eager—— 只要看起來可能(例如在視口內等弱信號)就開工。提升最大,但需要更嚴格的護欄。
把 eagerness 當作你家的 CFO:決定帶寬與 CPU要多積極地“超前投資”。
實踐建議:
- 頂部導航、分頁、上一篇/下一篇:eager;
- 鏈接密集、用戶意圖不明:moderate;
- 目標頁昂貴或網絡不穩:conservative。
目標選擇:列表 vs. 掃描文檔
你可以精確列 URL,也可以讓瀏覽器掃描當前文檔里的鏈接并按條件匹配。
精確列出:
<script type="speculationrules">
{ "prefetch": [{ "source": "list", "urls": ["/next", "/pricing"] }] }
</script>文檔驅動(按鏈接規則匹配):
<script type="speculationrules">
{
"prerender": [{
"source": "document",
"where": { "and": [ { "href_matches": "/(next|checkout)" } ] },
"eagerness": "eager"
}]
}
</script>文檔規則能一次聲明,處處生效,更適合規模化。
同源、跨子域,以及那個一定要加的響應頭
- Prerender 默認只限同源。
- 同站不同子域(a.example → b.example)想要 Prerender,需要目標頁主動同意,加上響應頭:
Supports-Loading-Mode: credentialed-prerender- 跨站目前仍受限,通常只能 Prefetch。
如果你在 DevTools 看到 “prerender canceled”,十有八九是目標頁沒加這個頭。
通過 HTTP 響應頭下發規則(便于 CDN / 灰度 / A/B)
規則不一定寫在 HTML,也可以由響應頭指向外部 JSON,讓運維或 CDN 獨立開關/抽樣。
HTML 響應頭:
Speculation-Rules: /speculationrules.json/speculationrules.json:
{
"prefetch": [{ "source": "document", "eagerness": "moderate" }],
"prerender": [{ "source": "document", "eagerness": "conservative" }]
}若單獨托管,請設置合理的 Content-Type,如:application/speculationrules+json。
護欄:讓 Prerender 可控、可上線
自查清單:
- 冪等性:導航行為不要在頁面加載時制造不可逆副作用(例如一進頁面就扣庫存)。
- 鑒權/狀態:把 Prerender 當做真實訪問,盡快驗證登錄,避免長時間阻塞。
- 分析埋點:推遲 pageview 到激活時發送,或標注為“預渲染”以防雙計數。
- 隱私數據:Prerender 在隱藏上下文里進行;只展示可被抓取卻未必被訪問也能接受的內容。
DevTools:怎么確認它真的在工作?
打開 Chrome DevTools → Preloading 面板: 你能看到 Speculation Rules 產生的 Prefetch/Prerender 嘗試以及取消原因(缺頭、CPU 降頻、后臺標簽……)。
這是最快的“為什么規則沒生效”的答案之書。
小技巧:加個僅在 document.prerendering === true 時顯示的調試角標,激活后自動消失:
if ("prerendering" in document) {
const t = document.createElement("div");
t.textContent = "(prerendered)";
t.style.cssText = "position:fixed;inset:auto 8px 8px auto;background:#000;color:#fff;padding:4px 6px;font:12px monospace;z-index:99999;opacity:.7";
document.body.appendChild(t);
document.addEventListener("prerenderingchange", () => t.remove());
}可直接復制的默認方案
1)先在同源鏈接上鋪一層 Prefetch:
<script type="speculationrules">
{ "prefetch": [{ "source": "document", "eagerness": "moderate" }] }
</script>2)對高置信路徑做 Prerender(如商品 → 結賬):
<script type="speculationrules">
{
"prerender": [{
"source": "document",
"where": { "href_matches": "/checkout" },
"eagerness": "eager"
}]
}
</script>3)為同站跨子域的目標頁加響應頭:
Supports-Loading-Mode: credentialed-prerender何時不要用 Prerender
- 一次性、超昂貴初始化且無法攤銷的頁面;
- 加載即產生副作用的頁面;
- 脆弱的三方腳本在后臺/隱藏上下文容易崩的頁面。
此類場景選 Prefetch,一樣能抹掉網絡往返帶來的等待。
移動端、網絡,以及你的 CDN 賬單
移動端也受益。瀏覽器會在資源緊張時自我節流: 后臺標簽、CPU 繁忙、電量偏低、網絡不佳……它會少做或不做推測性加載。
契約是:你聲明意圖,瀏覽器決定時機。 擔心帶寬?那就從 conservative 起步,觀察真實指標,再把某些關鍵鏈路提升到 moderate/eager。
性能的商業價值
這不是為了多 3 分的 Lighthouse,而是直接削掉跨頁的“首次可交互等待”。 更少的連點暴擊、更多的完成支付、更高的篇幅閱讀量。下一頁已熱身,用戶就不會流失在路上。
作戰手冊
- 先落地那 6 行的 Prerender 規則;
- 疊加 Prefetch 覆蓋更廣的同源鏈接;
- 同站跨子域的目標頁加頭
Supports-Loading-Mode: credentialed-prerender; - 通過 Speculation-Rules 響應頭下發/調參規則(便于灰度與 A/B);
- 去 DevTools → Preloading 看紅線,對癥修復。
不需要新框架,不需要插件。 只是你和瀏覽器成年人的協作。
我想聽你的實戰故事:
- eager 是否把你的流量包打爆?
- conservative 是否“像棉簽一樣軟”?
把實驗結果丟進評論區。
如果這招幫到了你,轉給那位還在膜拜加載動畫的同事;
也把這篇存進下個迭代的待辦里——當有人說“我們需要一個 loading 屏”時,你會用得上。
























