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

在本程式碼研究室中,我們透過預先載入和預先擷取一些資源,改善下列網頁的效能:

應用程式螢幕截圖

測量

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

  • 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 全螢幕

在 Glitch 的實際版本上執行 Lighthouse 效能稽核 (依序點選「Lighthouse」>「Options」>「Performance」) (另請參閱「透過 Lighthouse 找出效能改善機會」)。

Lighthouse 會針對延遲擷取的資源,顯示以下的失敗稽核:

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

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

「關鍵要求鏈結」代表瀏覽器優先處理及擷取資源的順序。這個網頁目前的樣貌如下:

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

由於 CSS 檔案位於要求鏈結的第 3 層,Lighthouse 將其視為較晚發現的資源。

預先載入重要資源

main.css 檔案是重要的資產,必須在網頁載入時立即使用。針對這類在應用程式中較晚擷取的重要檔案,請使用連結預先載入標記,在文件標頭中加入 Link 元素,通知瀏覽器提早下載。

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

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

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

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

網路面板,其中包含預先載入的資源

請注意,在 JavaScript 負責擷取該檔案之前,瀏覽器擷取 CSS 檔案的方式甚至已完成剖析。透過預先載入功能,瀏覽器會知道要針對資源進行預先擷取,並假設該資源對網頁至關重要。

如果使用方式不當,預先載入可能會對效能造成不利影響,因為系統會對未使用的資源提出不必要的要求。在這個應用程式中,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,系統也會提出擷取 details.css 的要求。

網路面板含有不必要的預先載入

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

控制台預先載入警告

您可以利用這項警告做為指標,找出網頁中未立即使用的預先載入資源。您現在可以移除這個網頁中不必要的預先載入連結。

<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>

如要瞭解這會如何觸發檔案要求,請在「DevTools」中開啟「Network」面板,然後取消勾選「Disable cache」選項。

在 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')

結論

透過本程式碼研究室,您應該已充分瞭解如何透過預先載入或預先擷取特定素材資源,改善網站的使用者體驗。請注意,這些技巧不應用於每項資源,且使用方式不當可能會影響效能。只需選擇性地預先載入或預先擷取,即可獲得最佳結果。

摘要:

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

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

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