利用程式碼分割功能減少 JavaScript 酬載

大部分網頁和應用程式由許多不同部分組成。只要將 JavaScript 分成多個區塊,就能改善網頁效能,而不是在載入第一個頁面後立即傳送構成應用程式的所有 JavaScript。

本程式碼研究室說明如何使用程式碼分割,改善可排序三個數字的簡單應用程式效能。

瀏覽器視窗中顯示名為「魔術排序工具」的應用程式,其中包含三個欄位,用於輸入數字和排序按鈕。

測量

一如以往,請務必先評估網站的成效,再嘗試加入任何最佳化設定。

  1. 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示 全螢幕
  2. 按下 `Control+Shift+J 鍵 (在 Mac 上為 Command+Option+J 鍵) 開啟開發人員工具。
  3. 按一下 [網路] 分頁標籤。
  4. 勾選「停用快取」核取方塊。
  5. 重新載入應用程式。

顯示 71.2 KB JavaScript 組合的網路面板。

71.2 KB 的 JavaScript,只是讓您在一個簡單的應用程式中排序幾個數字。 解決方案

在原始碼 (src/index.js) 中,lodash 程式庫會匯入並用於這個應用程式。Lodash 提供許多實用的公用程式函式,但這裡只會使用套件中的單一方法。安裝和匯入整個第三方依附元件,但只使用其中一小部分是很常見的錯誤。

最佳化

你可以透過下列幾種方式移除組合大小:

  1. 編寫自訂排序方法,而非匯入第三方程式庫
  2. 使用內建的 Array.prototype.sort() 方法進行數值排序
  3. 只從 lodash 匯入 sortBy 方法,而非整個程式庫
  4. 下載程式碼來分類,只在使用者點選按鈕時才進行排序

選項 1 和 2 是最適合用來縮減套件大小的方法 (這對於實際應用程式來說可能是最明智的選擇)。不過,為了教學 😈?,本課程不會使用這些內容。

選項 3 和選項 4 均有助於改善這個應用程式的效能。本程式碼研究室的後續幾節將介紹這些步驟。如同任何程式設計教學課程,請務必自行撰寫程式碼,而不要複製及貼上。

只匯入必要項目

您必須修改幾個檔案,使其只從 lodash 匯入單一方法。首先,請在 package.json 中替換此依附元件:

"lodash": "^4.7.0",

取代為:

"lodash.sortby": "^4.7.0",

現在在 src/index.js 中,匯入這個特定模組:

import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";

然後更新值的排序方式:

form.addEventListener("submit", e => {
  e.preventDefault();
  const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
  const sortedValues = _.sortBy(values);
  const sortedValues = sortBy(values);

  results.innerHTML = `
    <h2>
      ${sortedValues}
    </h2>
  `
});

重新載入應用程式,開啟開發人員工具,然後再次查看「Network」面板。

顯示 15.2 KB JavaScript 組合的網路面板。

就這個應用程式而言,套件大小縮減了 4 倍以上,而且幾乎沒有工作,但仍有進一步的改進空間。

程式碼分割

webpack 是現今最熱門的開放原始碼模組組合器之一。簡單來說,這項功能會將組成網頁應用程式的所有 JavaScript 模組 (及其他資產) 組合成靜態檔案,供瀏覽器讀取。

此應用程式中使用的單一套件可以分成兩個不同的區塊:

  • 其中一個負責構成初始路徑的程式碼
  • 包含排序程式碼的次要區塊

使用動態匯入時,次要區塊可以延遲載入或隨選載入。在這個應用程式中,只有在使用者按下按鈕時,才能載入組成區塊的程式碼。

請先移除 src/index.js 中排序方法的頂層匯入項目:

import sortBy from "lodash.sortby";

然後匯入當使用者按下按鈕時觸發的事件監聽器:

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

import() 功能是提案的一部分 (目前在 TC39 程序的第 3 階段),包含動態匯入模組的功能。Webpack 已支援這項功能,且採用提案所指的相同語法。

import() 會傳回承諾,並在解析後提供所選模組,並將其分割成單獨的區塊。傳回模組後,module.default 會用來參照 lodash 提供的預設匯出項目。承諾會與另一個呼叫 sortInput 方法的 .then 鏈結在一起,以便排序三個輸入值。承諾鏈的結束時,catch() 可用於處理承諾因錯誤而遭拒的情況。

最後,只要在檔案結尾編寫 sortInput 方法即可。此函式需為「傳回」一個函式,該函式會從 lodash.sortBy 匯入的方法中。接著,巢狀函式可以將三個輸入值排序,並更新 DOM。

const sortInput = () => {
  return (sortBy) => {
    const values = [
      input1.valueAsNumber,
      input2.valueAsNumber,
      input3.valueAsNumber
    ];
    const sortedValues = sortBy(values);

    results.innerHTML = `
      <h2>
        ${sortedValues}
      </h2>
    `
  };
}

監控

最後一次重新載入應用程式,並再次密切留意「Network」面板。應用程式載入時,只會下載小型的初始套件。

顯示 2.7 KB JavaScript 組合的網路面板。

按下按鈕排序輸入數字後,系統就會擷取並執行包含排序程式碼的區塊。

顯示 2.7 KB JavaScript 組合和 13.9 KB JavaScript 組合的網路面板。

您會發現數字仍是排序的!

結語

程式碼分割和延遲載入是非常實用的技巧,可縮減應用程式的初始組合大小,可直接使網頁載入時間更快。不過,在應用程式中納入這項最佳化作業前,您必須考量一些重要事項。

延遲載入 UI

如要延遲載入特定程式碼模組,請務必考量網路連線品質不佳者對他們的體驗。在使用者提交動作時,分割及載入非常大的程式碼區塊,可能會導致應用程式停止運作,因此請考慮顯示某種載入指標。

延遲載入第三方節點模組

在應用程式中延遲載入第三方依附元件並不是最佳做法,而是取決於您使用這類依附元件的位置。第三方依附元件通常會分割成獨立的 vendor 組合,由於其更新頻率較低,因此可快取。進一步瞭解 SplitChunksPlugin 如何協助您執行這項作業。

使用 JavaScript 架構延遲載入

許多使用 Webpack 的常用架構和程式庫都提供抽象化機制,比起在應用程式中間使用動態匯入功能,延遲載入更為簡單。

雖然瞭解動態匯入的運作方式十分實用,但請一律使用架構/程式庫建議的方法來延遲載入特定模組。

預先載入及預先擷取

請盡可能利用瀏覽器提示 (例如 <link rel="preload"><link rel="prefetch">),嘗試更快載入重要模組。Webpack 可以在匯入陳述式中使用魔術註解,以支援這兩種提示。詳情請參閱預先載入重要區塊指南。

延遲載入超過程式碼

圖片可構成應用程式的重要部分。延遲載入位於需捲動位置或裝置可視區域以外的廣告,可以加快網站速度。詳情請參閱 Lazysizes 指南。