看更多文章:
應用程式通常可以分為兩種類型的效能瓶頸:
I/O 限制: 這些應用程式將大部分時間花在處理輸入和輸出上。
CPU 限制: 這些應用程式將大部分時間花在運算任務上。
現在,這些分類如何轉化為前端應用程式的上下文,特別是 React 應用程式?
React 中的 I/O 效能挑戰
當涉及到 React 應用程式時,經常會出現 I/O 效能方面的問題,主要與非同步 HTTP 呼叫相關。無效地管理這些網路請求可能會導致應用程式速度減慢。雖然這篇文章主要關注 CPU 效能,但有必要簡要介紹一下可以找到 I/O 限制問題解決方案的關鍵領域:
盡可能實施延遲載入。
在初始載入資產和後端請求時請務必小心。
減少載入高度靜態元素的頻率(例如,選擇選項、配置)。
去抖特定請求的次數。
使用 Promise.all 等技術盡可能並行化請求。
透過優化資料庫存取等措施來提高關鍵後端端點的效率。
React 中的 CPU 效能挑戰
這篇文章的主旨是解決 React 中的 CPU 效能挑戰。在深入研究細節之前,讓我們先對效能建立一個具體的定義:
瀏覽器應用程式主要作為單執行緒程式執行。
腳本任務,例如 JavaScript 執行、DOM 渲染和事件處理,都發生在同一個執行緒。
緩慢的 JavaScript 模組可能會阻塞主執行緒。
如果主執行緒被阻塞,使用者介面將變得無回應,導致每秒幀數 (fps) 下降。
響應式 UI 的目標是至少 30 fps,理想情況下達到 60 fps,這意味著每幀的計算時間應在 30 毫秒或更短的時間內。
在 React 的背景下,這個問題變得至關重要。當觸發 React 元件更新時,整個子樹必須在 30 毫秒內渲染完畢。對於複雜而冗長的元件結構(例如表、樹和列表),這變得尤其具有挑戰性,可能需要大規模重新渲染。
反應渲染和提交階段
從高層次來看,React 分為兩個不同的階段:
渲染階段:
當元件更新時啟動,由 props 或 hooks 的變更觸發。
React 遍歷元件子樹,渲染每個子樹並計算虛擬 DOM (VDOM) 子樹。
只有受更新影響的「髒」子樹需要重新計算;更新元件的父元件可能不需要重新渲染。
此階段的效率與每個子元件的大小和計算成本成正比。
React.memo 可用於提供更有效率渲染流程的提示。
提交階段:
渲染階段產生整個 UI 的新虛擬 DOM。
在提交階段,React 將新樹與前一棵樹進行比較(VDOM 比較)。
React 計算反映新 VDOM 樹所需的最小 DOM 突變。
套用 DOM 突變,更新 UI。
預設情況下,此階段本質上是高效的。
整個過程必須在 30 或 16 毫秒內完成(分別針對 30 fps 和 60 fps),UI 才會被視為回應。工作負載與應用程式的大小成正比。
後續探索將聚焦在提升Render階段的效率。在深入研究優化技術之前,了解如何測量和辨識應用程式中的緩慢元件至關重要。
測量
我經常依賴的工具包括:
Chrome 開發工具的效能標籤
React Dev Tool 的效能標籤
Chrome 開發工具的效能標籤
該工具作為適用於任何瀏覽器應用程式的綜合資源而脫穎而出。它提供對每秒幀數的洞察、捕獲堆疊追蹤、辨識程式碼的慢速或熱點部分等等。主要使用者介面由火焰圖表示。
若要深入了解套用於 React 的 Chrome 效能選項卡,請參閱此文件。
React 開發工具的效能標籤
若要利用此工具,您需要在瀏覽器中安裝 React Dev Tool 擴充功能。它專門針對 React 客製化了 Chrome 開發工具效能標籤中的資訊。透過火焰圖,您可以觀察不同的提交階段以及在對應渲染階段執行的 JavaScript 程式碼。
該工具有助於輕鬆確定:
當元件進行重新渲染時。
哪些道具改變了。
哪些鉤子發生了變化,包括狀態、上下文等等。有關更多詳細訊息,請參閱介紹性帖子。
測量方法
這是我在評估前端應用程式時更喜歡的方法:
確定問題:
建立一個假設:
測量:
測量(第二部分):
建立解決方案:
測量解決方案:
在沒有適當衡量的情況下進行最佳化會使努力實際上變得無效。雖然有些問題可能很明顯,但大多數問題都需要徹底的測量,從而構成性能增強過程的基石。
此外,測量可讓您向上傳達成果,向使用者、利害關係人和您的領導層通報應用程式特定領域內實現的效能改進(以百分比增益表示)。
React 應用程式中 CPU 限制問題的通用解決方案
現在有了測量結果並了解了問題領域,讓我們深入研究潛在的解決方案。優化 React 效能圍繞著改進渲染的元件和渲染的元件。
許多效能問題也源自於反模式。消除這些反模式(例如避免渲染方法中的內聯函數定義)有助於提高渲染時間。事實上,解決不良模式可以降低複雜性並同時提高效能。
🤔 改進元件渲染
辨識 React 應用程式中的緩慢元件通常指的是難以渲染或單一頁面上實例數量過多的特定元件。多種原因可能導致他們行動遲緩:
元件內的阻塞計算。
渲染大型元件樹。
使用昂貴或低效率的函式庫。
大多數這些問題都歸結為提高元件渲染的速度。有時,關鍵元件不能依賴過於複雜的函式庫,需要回歸基本原則並實施更簡單的替代方案。
例如,我在複雜表格中每行的多個單元格中過度使用 Formik 時遇到了此類挑戰。雖然提高單一元件的效率還有很長的路要走,但注意力最終必須轉移到正在渲染的元件上。
🧙 改進哪些元件渲染
這方面提供了兩大類改進:
虛擬化:
道具優化:
反應.備忘錄:
React 中的大多陣列件都可以被記憶,確保使用相同的 props,元件返回相同的樹(儘管鉤子、狀態和上下文仍然受到尊重)。如果它們的 props 保持不變,則利用 React.memo
通知 React 跳過重新渲染這些已記憶的元件。
import React from 'react';
const MyComponent = React.memo((props) => {
// Component logic here
});
export default MyComponent;
假道具更改:useCallback:
import React, { useCallback } from 'react';
const MyComponent = () => {
const onChange = useCallback((e) => console.log(e), []);
return <input onChange={onChange} />;
};
export default MyComponent;
假道具更改:useMemo:
在將複雜資料結構作為 props 傳遞之前,在沒有適當記憶的情況下建立複雜的資料結構時,也會出現類似的挑戰。利用「useMemo」可確保僅在依賴項發生變更時才重新計算行,從而提高效率。
import React, { useMemo } from 'react';
const MyComponent = ({ data, deps }) => {
const rows = useMemo(() => data.filter(bySearchCriteria).sort(bySortOrder), [deps]);
return <Table data={rows} />;
};
export default MyComponent;
雖然您可以靈活地自訂「React.memo」如何比較目前與先前的道具,但保持快速運算至關重要,因為它是渲染階段不可或缺的一部分。避免在每次渲染期間過於複雜的深度比較。
它在 React 開發工具中的樣子:
他們真的嗎?它們是假的道具更改嗎?使用useCallback
和useMemo
。
它在 React 開發工具中的樣子:
使用“React.memo”來記住您的純元件。
它在 React 開發工具中的樣子:
這裡沒有什麼太明顯的事情要做。嘗試驗證更改的鉤子是否有意義。也許一個糟糕的上下文提供者正在以與假道具更改可能出現的方式相同的方式偽造更改。
與此類似,我個人在 Slack 上經營著一個由開發人員主導的社群。我們在其中討論這些類型的實現、整合、一些真相炸彈、奇怪的聊天、虛擬會議以及一切有助於開發人員保持理智的事情;)畢竟,太多的知識也可能是危險的。
我邀請您加入我們的免費社區,參與討論,並分享您的驚人經驗和專業知識。您可以填寫此表格,Slack 邀請將在幾天後收到您的電子郵件。我們擁有來自一些偉大公司(Atlassian、Scaler、Cisco、IBM 等)的出色人員,您一定不想錯過與他們的互動。 邀請表
您可能想要查看整合通知基礎架構的無縫方式。
https://github.com/suprsend/suprsend-go