JS 效能優化 Performance
Bundle splitting 綑綁分割
將程式碼分割成較小的 bundle,從而減少初始載入時間,例如:將網頁初始所需的程式碼和點擊才需要處理的程式碼分開成兩包,如此,網頁初始渲染所需的程式碼減少,載入時間減少
壓縮 Javascript
壓縮有助於減少網路傳輸檔案的時間。Gzip 和 Brotli 是最常見的 JavaScript 壓縮方法,並受到現代瀏覽器的廣泛支援。如果使用 Webpack 捆綁程式碼,則可以使用 CompressionPlugin
進行 Gzip 壓縮,或使用 BrotliWebpackPlugin
進行 Brotli 壓縮。
module.exports = {
plugins: [
//...
new CompressionPlugin(),
],
};
另外,也可以透過Minification
刪除空格或不必要的程式碼如:換行、縮排或註解等,才進一步減少檔案大小。Terser
是 ES6 熱門的 JS 壓縮工具,Webpack v4 預設包含此套件工具,舊版 webpack 也可以使用 TerserWebpackPlugin
載入資源優化
加載資源的方法有下面幾種:
- 立即載入(正常方式)
- lazy(基於 route) - 當使用者移動到特定 route 或元件時載入
- lazy(基於 interaction) - 當使用者點擊 UI 時載入,例如:youtube 點擊影片才加載
- lazy(in viewport) - 當使用者滾動到元件時加載
- prefetch - 在需要之前加載,但在加載了關鍵資源後
- preload - 有一定急迫性
dynamic import 動態載入
由於不是每個功能都會使用到,在 React 專案中 便可以使用 React lazy 和 React suspense 將程式碼拆分成不同的區塊延遲載入 ,優先載入比較重要的 js, 再載入次要的 js
Lazy
動態載入 js,可以將程式碼分割,需要用到時再用 import 載入Suspense
延遲載入 component 會被 suspense 包覆 ,假如 component 還沒載入完成, 就能夠透過 fallback 顯示一些提示訊息,必須搭配 Lazy 使用,如果只有寫 Lazy 沒有 Suspense 的話就會報錯
import React, { lazy, Suspense } from "react";
const InputForm = lazy(() => import("../component/InputForm"));
const FormPage = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<InputForm />
</Suspense>
);
};
export default FormPage;
但 SSR 尚未支援 lazy 和 React Suspense,這時候可以使用 loadable-components
函式庫
import React from "react";
import loadable from "@loadable/component";
import Send from "./icons/Send";
import Emoji from "./icons/Emoji";
const EmojiPicker = loadable(() => import("./EmojiPicker"), {
fallback: <div id="loading">Loading...</div>,
});
const ChatInput = () => {
const [pickerOpen, togglePicker] = React.useReducer((state) => !state, false);
return (
<div className="chat-input-container">
<input type="text" placeholder="Type a message..." />
<Emoji onClick={togglePicker} />
{pickerOpen && <EmojiPicker />}
<Send />
</div>
);
};
export default ChatInput;
互動載入 Import on Interaction
例如:使用者點擊 emoji 才跳出表情選單
import React, { useState, createElement } from "react";
import MessageList from "./MessageList";
import MessageInput from "./MessageInput";
import ErrorBoundary from "./ErrorBoundary";
const Channel = () => {
const [emojiPickerEl, setEmojiPickerEl] = useState(null);
const openEmojiPicker = () => {
import(/* webpackChunkName: "emoji-picker" */ "./EmojiPicker")
.then((module) => module.default)
.then((emojiPicker) => {
setEmojiPickerEl(createElement(emojiPicker));
});
};
const closeEmojiPickerHandler = () => {
setEmojiPickerEl(null);
};
return (
<ErrorBoundary>
<div>
<MessageList />
<MessageInput onClick={openEmojiPicker} />
{emojiPickerEl}
</div>
</ErrorBoundary>
);
};
Import On Visibility 視口載入
最常見的情況是有顯示圖片庫,一開始只載入需要呈現在畫面上的圖片,當使用者向下滾動時,才動態載入後面的圖片。可以使用 IntersectionObserver
API 或 react-lazyload
函式庫。
prefetch 預先取回
預先取回連結(Prefetch)是一項瀏覽器機制。這項機制利用瀏覽器閒置時間,預先下載取回使用者稍後可能造訪的網頁資源,瀏覽器會將這些提前下載的資源儲存在本地 cache 中,但不執行解析
<link rel="”prefetch“" />