1. 程式人生 > 程式設計 >使用preload預載入頁面資源時注意事項

使用preload預載入頁面資源時注意事項

preload 提供了一種宣告式的命令,讓瀏覽器提前載入指定資源(載入後並不執行),在需要執行的時候再執行。提供的好處主要是

將載入和執行分離開,可不阻塞渲染和 document 的 onload 事件

提前載入指定資源,不再出現依賴的 font 字型隔了一段時間才刷出

如何使用 preload

使用 link 標籤建立

<!-- 使用 link 標籤靜態標記需要預載入的資源 -->
<link rel="preload" href="/path/to/style.css" rel="external nofollow" 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" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
<link rel="preload" as="script" href="https://a.xxx.com/xxx/PcCommon.js" rel="external nofollow" >
<link rel="preload" as="script" href="https://a.xxx.com/xxx/TabsPc.js" rel="external nofollow" >

如何判斷瀏覽器是否支援 preload

目前我們支援的瀏覽器主要為高版本 Chrome,所以可放心使用 preload 技術。 其他環境在 caniuse.com 上查到的支援情況如下:

使用preload預載入頁面資源時注意事項

在不支援 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 的資源載入順序將被提前:

使用preload預載入頁面資源時注意事項

避免濫用 preload

使用 preload 後,Chrome 會有一個警告:

使用preload預載入頁面資源時注意事項

如上文所言,若不確定資源是必定會載入的,則不要錯誤使用 preload,以免本末倒置,給頁面帶來更沉重的負擔。

當然,可以在 PC 中使用 preload 來重新整理資源的快取,但在移動端則需要特別慎重,因為可能會浪費使用者的頻寬。

避免混用 preload 和 prefetch

preload 和 prefetch 混用的話,並不會複用資源,而是會重複載入。

<link rel="preload" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" as="font">
<link rel="prefetch" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" as="font">

使用 preload 和 prefetch 的邏輯可能不是寫到一起,但一旦發生對用一資源 preload 或 prefetch 的話,會帶來雙倍的網路請求,這點通過 Chrome 控制檯的網路面板就能甄別:

使用preload預載入頁面資源時注意事項

避免錯用 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" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >

然而我發現這個檔案載入了兩次:

使用preload預載入頁面資源時注意事項

使用preload預載入頁面資源時注意事項

使用preload預載入頁面資源時注意事項

原因是對跨域的檔案進行 preload 的時候,我們必須加上 crossorigin 屬性:

<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >

再看一下網路請求,就變成一條了。

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.

那為何會有兩條請求,且優先順序不一致,又沒有命中快取呢?這就得引出下一個話題來解釋了。

不同資源載入的優先順序規則

我們先來看一張圖:

使用preload預載入頁面資源時注意事項

這張表詳見:Chrome Resource Priorities and Scheduling

這張圖表示的是,在 Chrome 46 以後的版本中,不同的資源在瀏覽器渲染的不同階段進行載入的優先順序。 在這裡,我們只需要關注 DevTools Priority 體現的優先順序,一共分成五個級別:

Highest 最高

Hight 高

Medium 中等

Low 低

Lowest 最低

使用preload預載入頁面資源時注意事項

html 主要資源,其優先順序是最高的

使用preload預載入頁面資源時注意事項

使用preload預載入頁面資源時注意事項

css 樣式資源,其優先順序也是最高的

使用preload預載入頁面資源時注意事項

CSS(match) 指的是對已有的 DOM 具備規則的有效的樣式檔案。

使用preload預載入頁面資源時注意事項

script 指令碼資源,優先順序不一

使用preload預載入頁面資源時注意事項

使用preload預載入頁面資源時注意事項

前三個 js 檔案是寫死在 html 中的靜態資源依賴,後三個 js 檔案是根據首屏按需非同步載入的元件資源依賴,這正驗證了這個規則。

font 字型資源,優先順序不一

使用preload預載入頁面資源時注意事項

使用preload預載入頁面資源時注意事項

樣式檔案中有一個 @font-face 依賴一個 font 檔案,樣式檔案中依賴的字型檔案載入的優先順序是 Highest; 在使用 preload 預載入這個 font 檔案時,若不指定 crossorigin 屬性(即使同源),則會採用匿名模式的 CORS 去載入,優先順序是 High,看下圖對比: 第一條 High 優先順序也就是 preload 的請求:

使用preload預載入頁面資源時注意事項

第二條 Highest 也就是樣式引入的請求:

使用preload預載入頁面資源時注意事項

可以看到,在 preload 的請求中,缺少了一個 origin 的請求頭欄位,表示這個請求是匿名的請求。 讓這兩個請求能共用快取的話,目前的解法是給 preload 加上 crossorigin 屬性,這樣請求頭會帶上 origin,且與樣式引入的請求同源,從而做到命中快取:

<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >

這麼請求就只剩一個:

使用preload預載入頁面資源時注意事項

使用preload預載入頁面資源時注意事項

在網路瀑布流圖中,也顯示成功預載入且後續命中快取不再二次載入:

使用preload預載入頁面資源時注意事項

總結

preload 是個好東西,能告訴瀏覽器提前載入當前頁面必須的資源,將載入與解析執行分離開,做得好可以對首次渲染帶來不小的提升,但要避免濫用,區分其與 prefetch 的關係,且需要知道 preload 不同資源時的網路優先順序差異。

preload 載入頁面必需的資源如 CDN 上的字型檔案,與 prefetch 預測載入下一屏資料,興許是個不錯的組合。

更多關於preload預載入頁面的文章大家可以看看下面的相關連結