程式碼研究室:預先載入重要資產,藉此提升載入速度

Houssein Djirdeh
Houssein Djirdeh

在本程式碼研究室中,我們將預先載入及預先擷取一些資源,藉此提升下列網頁的效能:

應用程式螢幕截圖

測量

請先評估網站成效,再進行任何最佳化調整。

  • 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示 全螢幕

在 Glitch 的即時版本上執行 Lighthouse 成效稽核 (依序點選「Lighthouse」>「Options」>「Performance」)。(另請參閱「透過 Lighthouse 找出成效提升機會」)。

如果資源擷取時間較晚,Lighthouse 會顯示下列稽核失敗結果:

Lighthouse:預先載入關鍵要求稽核
  • 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  • 按一下 [網路] 分頁標籤。
「網路」面板,其中包含稍晚才發現的資源

HTML 文件中的 Link 元素 (<link>) 不會擷取 main.css 檔案,但個別的 JavaScript 檔案 fetch-css.js 會在 window.onLoad 事件後,將 Link 元素附加至 DOM。也就是說,瀏覽器完成剖析及執行 JS 檔案後,才會擷取檔案。同樣地,只有在 CSS 檔案下載完成後,才會擷取 main.css 內指定的網頁字型 (K2D.woff2)。

關鍵要求鏈結代表瀏覽器優先擷取的資源順序。這個網頁目前看起來如下:

├─┬ / (initial HTML file)
  └── fetch-css.js
    └── main.css
      └── K2D.woff2

由於 CSS 檔案位於要求鏈的第三層,Lighthouse 已將其識別為延遲探索到的資源。

預先載入重要資源

main.css 檔案是網頁載入後立即需要的關鍵資產。對於這類在應用程式中稍晚才擷取的重要檔案,請使用連結預先載入標記,在文件的標題中新增 Link 元素,通知瀏覽器提早下載。

為這個應用程式新增預先載入標記:

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
</head>

as 屬性用於識別要擷取的資源類型,as="style" 則用於預先載入樣式表檔案。

重新載入應用程式,並查看開發人員工具中的「網路」面板。

預先載入資源的「網路」面板

請注意,瀏覽器會在負責擷取 CSS 檔案的 JavaScript 甚至還沒完成剖析時,就擷取該檔案。透過預先載入,瀏覽器會知道要預先擷取資源,並假設該資源對網頁至關重要。

如果使用不當,預先載入可能會對效能造成負面影響,因為系統會對未使用的資源發出不必要的要求。在這個應用程式中,details.css 是位於專案根目錄的另一個 CSS 檔案,但用於獨立的 /details route。如要顯示預先載入的錯誤用法範例,請一併為這項資源新增預先載入提示。

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

重新載入應用程式,然後查看「Network」面板。 即使網頁未使用 details.css,系統仍會要求擷取。

網路面板,其中包含不必要的預先載入

如果預先載入的資源在網頁載入後幾秒內未使用,Chrome 會在「控制台」面板中顯示警告。

控制台中的預先載入警告

您可以根據這項警告,判斷是否有任何預先載入的資源未立即供網頁使用。現在可以移除這個網頁不必要的預先載入連結。

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

如要查看可擷取的所有資源類型清單,以及 as 屬性應使用的正確值,請參閱 MDN 預先載入文章

預先擷取未來資源

預先擷取是另一項瀏覽器提示,可用於要求取得其他導覽路徑使用的資產,但優先順序低於目前網頁所需的重要資產。

在這個網站中,點選圖片會前往另一個details/ 路徑。

詳細路線

另一個 CSS 檔案 details.css 包含這個簡單網頁所需的所有樣式。在 index.html 中新增連結元素,預先擷取這項資源。

<head>
  <!-- ... -->
  <link rel="prefetch" href="details.css">
</head>

如要瞭解這項操作如何觸發檔案要求,請在開發人員工具中開啟「網路」面板,然後取消勾選「停用快取」選項。

在 Chrome 開發人員工具中停用快取

重新載入應用程式,並注意在擷取所有其他檔案後,系統如何對 details.css 發出優先順序極低的要求。

網路面板,其中包含預先擷取的資源

開啟開發人員工具後,按一下網站上的圖片,即可前往 details 頁面。 由於 details.html 中使用連結元素擷取 details.css,因此系統會如預期對資源提出要求。

詳細資料頁面的網路要求

在開發人員工具中按一下 details.css 網路要求,即可查看詳細資料。你會發現檔案是從瀏覽器的磁碟快取中擷取。

從磁碟快取擷取的詳細資料要求

預先擷取功能會利用瀏覽器的閒置時間,提早要求其他網頁所需的資源。這樣一來,瀏覽器就能更快快取資產,並在需要時從快取提供資產,加快日後的導覽要求。

使用 webpack 預先載入和預先擷取

使用程式碼分割減少 JavaScript 酬載」一文探討如何使用動態匯入,將套件分割成多個區塊。我們將以簡單的應用程式為例,說明如何動態匯入 Lodash 中的模組 (表單提交時)。

示範程式碼分割的 Magic Sorter 應用程式

您可以在這裡存取這個應用程式的 Glitch。

下列程式碼區塊位於 src/index.js, 中,負責在點選按鈕時動態匯入方法。

form.addEventListener("submit", e => {
  e.preventDefault()
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

分割套件可減少初始大小,進而縮短網頁載入時間。webpack 4.6.0 版支援預先載入或預先擷取動態匯入的區塊。以這個應用程式為例,lodash 方法可在瀏覽器閒置時預先擷取;使用者按下按鈕時,系統不會延遲擷取資源。

在動態匯入中使用特定的 webpackPrefetch 註解參數,預先擷取特定區塊。 以下是這個特定應用程式的顯示畫面。

form.addEventListener("submit", e => {
  e.preventDefault()
  import(/* webpackPrefetch: true */ 'lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

應用程式重新載入後,webpack 會將資源的預先擷取代碼插入文件標題。您可以在開發人員工具的「元素」面板中查看這項資訊。

包含預先擷取標記的「元素」面板

在「網路」面板中觀察要求,也會發現這個區塊是在所有其他資源要求後,以低優先順序擷取。

「網路」面板,其中包含預先擷取的要求

雖然預先擷取更適合這個用途,但 webpack 也支援預先載入動態匯入的區塊。

import(/* webpackPreload: true */ 'module')

結論

完成本程式碼研究室後,您應該就能充分瞭解預先載入或預先擷取特定資產,如何改善網站的使用者體驗。請務必注意,這些技術不適用於所有資源,使用不當可能會損害效能。只預先載入或預先擷取特定資源,才能獲得最佳成效。

摘要:

  • 對於稍晚才發現但對目前網頁至關重要的資源,請使用預先載入
  • 針對未來導覽路徑或使用者動作所需的資源,使用預先擷取

目前並非所有瀏覽器都支援預先載入和預先擷取。也就是說,並非所有應用程式使用者都能感受到效能提升。

如要進一步瞭解預先載入和預先擷取對網頁特定層面的影響,請參閱下列文章: