大多數網頁和應用程式都由許多不同部分組成。請將 JavaScript 分割成多個區塊,以便改善網頁效能,而非在載入第一個網頁時立即傳送組成應用程式的所有 JavaScript。
本程式碼研究室將說明如何使用程式碼分割功能,改善排序三個數字的簡易應用程式效能。
測量
如同往常,在嘗試新增任何最佳化功能前,請先評估網站的效能。
- 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 。
- 按下 `Control + Shift + J` 鍵 (在 Mac 上為 `Command + Option + J` 鍵) 開啟開發人員工具。
- 按一下 [網路] 分頁標籤。
- 勾選「Disable cache」核取方塊。
- 重新載入應用程式。
只為了在簡單的應用程式中排序幾個數字,就需要 71.2 KB 的 JavaScript。發生什麼事了?
在原始碼 (src/index.js
) 中,lodash
程式庫會匯入並用於此應用程式。Lodash 提供許多實用的公用函式,但這裡只會使用該套件中的單一方法。安裝及匯入整個第三方依附元件,但只使用其中的一小部分,這是常見的錯誤。
最佳化
您可以透過幾種方式縮減套件大小:
- 撰寫自訂排序方法,而非匯入第三方程式庫
- 使用內建的
Array.prototype.sort()
方法以數字排序 - 只匯入
lodash
中的sortBy
方法,而非整個程式庫 - 只有在使用者按下按鈕時,才下載排序程式碼
選項 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>
`
});
重新載入應用程式、開啟 DevTools,然後再次查看「Network」面板。
對於這個應用程式,只要花費很少心力,就能將套件大小縮減超過 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 提供的預設匯出項目。承諾會與另一個 .then
連結,該 .then
會叫用 sortInput
方法來排序三個輸入值。在承諾鏈結結束時,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」面板。應用程式載入後,系統只會下載小型初始套件。
按下按鈕排序輸入的數字後,系統會擷取並執行包含排序程式碼的區塊。
請注意,數字仍會排序!
結論
程式碼分割和延遲載入是極為實用的技巧,可縮減應用程式初始套件大小,進而直接縮短網頁載入時間。不過,在應用程式中加入這項最佳化功能前,您必須考量一些重要事項。
延遲載入 UI
在延遲載入特定程式碼模組時,請務必考量網路連線較弱的使用者體驗。在使用者提交動作時,分割及載入大量程式碼,可能會讓應用程式看起來停止運作,因此建議您顯示某種載入指標。
延遲載入第三方節點模組
在應用程式中延後載入第三方依附元件不一定是最佳做法,這取決於您使用依附元件的地點。通常,第三方依附元件會分割為個別的 vendor
套件,因為這些套件不會經常更新,因此可以快取。進一步瞭解 SplitChunksPlugin 如何協助您執行這項操作。
使用 JavaScript 架構的延遲載入
許多使用 webpack 的熱門架構和程式庫都提供抽象化功能,讓延遲載入功能比在應用程式中間使用動態匯入功能更容易。
雖然瞭解動態匯入的運作方式很有幫助,但請一律使用架構/程式庫建議的方法,以便延後載入特定模組。
預先載入和預先擷取
盡可能利用瀏覽器提示 (例如 <link rel="preload">
或 <link rel="prefetch">
),盡早嘗試載入重要模組。webpack 會在匯入陳述式中使用魔法註解,支援這兩種提示。詳情請參閱「預先載入重要區塊」指南。
延遲載入不只限於程式碼
圖片可能會成為應用程式的重要部分。延遲載入折疊下方或裝置可視區域外的內容,可以加快網站速度。詳情請參閱 Lazysizes 指南。