作者簡介 felix 螞蟻金服·資料體驗技術團隊
本文主要介紹preload的使用,以及與prefetch的區別。然後會聊聊瀏覽器的載入優先順序。
preload 提供了一種宣告式的命令,讓瀏覽器提前載入指定資源(載入後並不執行),在需要執行的時候再執行。提供的好處主要是
- 將載入和執行分離開,可不阻塞渲染和 document 的 onload 事件
- 提前載入指定資源,不再出現依賴的font字型隔了一段時間才刷出
如何使用 preload
使用 link 標籤建立
<!-- 使用 link 標籤靜態標記需要預載入的資源 -->
<link rel="preload" href="/path/to/style.css" as="style">
<!-- 或使用指令碼動態建立一個 link 標籤後插入到 head 頭部 -->
<script>
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'style';
link.href = '/path/to/style.css';
document.head.appendChild(link);
</script>
複製程式碼
使用 HTTP 響應頭的 Link 欄位建立
Link: <https://example.com/other/styles.css>; rel=preload; as=style
複製程式碼
如我們常用到的 antd 會依賴一個 CDN 上的 font.js 字型檔案,我們可以設定為提前載入,以及有一些模組雖然是按需非同步載入,但在某些場景下知道其必定會載入的,則可以設定 preload 進行預載入,如:
<link rel="preload" as="font" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">
<link rel="preload" as="script" href="https://a.xxx.com/xxx/PcCommon.js">
<link rel="preload" as="script" href="https://a.xxx.com/xxx/TabsPc.js">
複製程式碼
如何判斷瀏覽器是否支援 preload
目前我們支援的瀏覽器主要為高版本 Chrome,所以可放心使用 preload 技術。 其他環境在 caniuse.com 上查到的支援情況如下:
在不支援 preload 的瀏覽器環境中,會忽略對應的 link 標籤,而若需要做特徵檢測的話,則:const isPreloadSupported = () => {
const link = document.createElement('link');
const relList = link.relList;
if (!relList || !relList.supports) {
return false;
}
return relList.supports('preload');
};
複製程式碼
如何區分 preload 和 prefetch
- preload 是告訴瀏覽器頁面必定需要的資源,瀏覽器一定會載入這些資源;
- prefetch 是告訴瀏覽器頁面可能需要的資源,瀏覽器不一定會載入這些資源。
preload 是確認會載入指定資源,如在我們的場景中,x-report.js 初始化後一定會載入 PcCommon.js 和 TabsPc.js, 則可以預先 preload 這些資源;
prefetch 是預測會載入指定資源,如在我們的場景中,我們在頁面載入後會初始化首屏元件,當使用者滾動頁面時,會拉取第二屏的元件,若能預測使用者行為,則可以 prefetch 下一屏的元件。
preload 將提升資源載入的優先順序
使用 preload 前,在遇到資源依賴時進行載入:
使用 preload 後,不管資源是否使用都將提前載入: 可以看到,preload 的資源載入順序將被提前:避免濫用 preload
使用 preload 後,Chrome 會有一個警告:
如上文所言,若不確定資源是必定會載入的,則不要錯誤使用 preload,以免本末倒置,給頁面帶來更沉重的負擔。
當然,可以在 PC 中使用 preload 來重新整理資源的快取,但在移動端則需要特別慎重,因為可能會浪費使用者的頻寬。
避免混用 preload 和 prefetch
preload 和 prefetch 混用的話,並不會複用資源,而是會重複載入。
<link rel="preload" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" as="font">
<link rel="prefetch" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" as="font">
複製程式碼
使用 preload 和 prefetch 的邏輯可能不是寫到一起,但一旦發生對用一資源 preload 或 prefetch 的話,會帶來雙倍的網路請求,這點通過 Chrome 控制檯的網路皮膚就能甄別:
避免錯用 preload 載入跨域資源
若 css 中有應用於已渲染到 DOM 樹的元素的選擇器,且設定了 @font-face 規則時,會觸發字型檔案的載入。 而字型檔案載入中時,DOM 中的這些元素,是處於不可見的狀態。對已知必載入的 font 檔案進行預載入,除了有效能提升外,更有體驗優化的效果。
在我們的場景中,已知 antd.css 會依賴 font 檔案,所以我們可以對這個字型檔案進行 preload:
<link rel="preload" as="font" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">
複製程式碼
然而我發現這個檔案載入了兩次:
原因是對跨域的檔案進行 preload 的時候,我們必須加上 crossorigin 屬性:
<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">
複製程式碼
再看一下網路請求,就變成一條了。
W3 規範是這麼解釋的:
Preload links for CORS enabled resources, such as fonts or images with a crossorigin attribute, must also include a crossorigin attribute, in order for the resource to be properly used.
那為何會有兩條請求,且優先順序不一致,又沒有命中快取呢?這就得引出下一個話題來解釋了。
不同資源載入的優先順序規則
我們先來看一張圖:
這張表詳見:Chrome Resource Priorities and Scheduling
這張圖表示的是,在 Chrome 46 以後的版本中,不同的資源在瀏覽器渲染的不同階段進行載入的優先順序。 在這裡,我們只需要關注 DevTools Priority 體現的優先順序,一共分成五個級別:
- Highest 最高
- Hight 高
- Medium 中等
- Low 低
- Lowest 最低
html 主要資源,其優先順序是最高的
css 樣式資源,其優先順序也是最高的
CSS(match) 指的是對已有的 DOM 具備規則的有效的樣式檔案。script 指令碼資源,優先順序不一
前三個 js 檔案是寫死在 html 中的靜態資源依賴,後三個 js 檔案是根據首屏按需非同步載入的元件資源依賴,這正驗證了這個規則。font 字型資源,優先順序不一
css 樣式檔案中有一個 @font-face 依賴一個 font 檔案,樣式檔案中依賴的字型檔案載入的優先順序是 Highest; 在使用 preload 預載入這個 font 檔案時,若不指定 crossorigin 屬性(即使同源),則會採用匿名模式的 CORS 去載入,優先順序是 High,看下圖對比: 第一條 High 優先順序也就是 preload 的請求:第二條 Highest 也就是樣式引入的請求:
可以看到,在 preload 的請求中,缺少了一個 origin 的請求頭欄位,表示這個請求是匿名的請求。 讓這兩個請求能共用快取的話,目前的解法是給 preload 加上 crossorigin 屬性,這樣請求頭會帶上 origin, 且與樣式引入的請求同源,從而做到命中快取:
<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">
複製程式碼
這麼請求就只剩一個:
在網路瀑布流圖中,也顯示成功預載入且後續命中快取不再二次載入:總結
preload 是個好東西,能告訴瀏覽器提前載入當前頁面必須的資源,將載入與解析執行分離開,做得好可以對首次渲染帶來不小的提升,但要避免濫用,區分其與 prefetch 的關係,且需要知道 preload 不同資源時的網路優先順序差異。
preload 載入頁面必需的資源如 CDN 上的字型檔案,與 prefetch 預測載入下一屏資料,興許是個不錯的組合。
參考資料:
- https://www.w3.org/TR/preload/
- https://www.w3.org/TR/resource-hints/
- Prioritizing Your Resources with link rel='preload'
- Preload, Prefetch And Priorities in Chrome
- A Link: rel=preload Analysis From the Chrome Data Saver Team
團隊提前給大家拜個早年,感謝大家對於專欄的支援~對團隊感興趣的同學可以關注專欄或者傳送簡歷至'tao.qit####alibaba-inc.com'.replace('####', '@'),歡迎有志之士加入~