Skip to main content

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 才跳出表情選單

Alt text

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“" />

preload 預加載