前端開發 React 組件優化

1.?使用 React.memo 進行組件優化

問題:當父組件重新渲染時,子組件也會重新渲染,即使它的 props 沒有變化。

解決方案:使用 React.memo 包裹子組件,讓其只在 props 變化時才重新渲染。

示例場景:展示一個顯示計數的組件,只有計數值變化時才會重新渲染。

import React, { useState } from 'react';const ChildComponent = React.memo(({ count }) => {console.log('ChildComponent rendered');return <div>Count: {count}</div>;
});function ParentComponent() {const [count, setCount] = useState(0);const [text, setText] = useState('');return (<div><ChildComponent count={count} /><button onClick={() => setCount(count + 1)}>Increase Count</button><inputtype="text"value={text}onChange={e => setText(e.target.value)}placeholder="Type something"/></div>);
}export default ParentComponent;

在這個例子中,即使輸入框內容改變,ChildComponent 也不會重新渲染,因為它被 React.memo 包裹,只會在 count 變化時重新渲染。

2.?使用 useCallback 和 useMemo 優化函數和計算

問題:在函數式組件中,每次渲染都會創建新的函數和計算,導致不必要的渲染和性能浪費。

解決方案:使用 useCallback 緩存函數,useMemo 緩存計算結果。

示例場景:避免函數在不必要的情況下被重新創建。

import React, { useState, useCallback, useMemo } from 'react';function ExpensiveComponent({ onClick }) {console.log('ExpensiveComponent rendered');return <button onClick={onClick}>Click me</button>;
}function ParentComponent() {const [count, setCount] = useState(0);const handleClick = useCallback(() => {console.log('button clicked');}, []);const computedValue = useMemo(() => {console.log('computed value');return count * 2;}, [count]);return (<div><ExpensiveComponent onClick={handleClick} /><div>Computed Value: {computedValue}</div><button onClick={() => setCount(count + 1)}>Increase Count</button></div>);
}export default ParentComponent;

在這個例子中,handleClick 和 computedValue 只會在依賴項變化時重新創建,從而避免不必要的渲染和計算。

3.?避免不必要的 Re-render

問題:由于父組件的狀態或 props 改變,導致子組件不必要地重新渲染。

解決方案:拆分組件,使用 React.memo 或 shouldComponentUpdate,并確保 key 使用合理。

示例場景:優化子組件渲染邏輯。

import React, { useState } from 'react';const ListItem = React.memo(({ item }) => {console.log('ListItem rendered:', item);return <li>{item}</li>;
});function ParentComponent() {const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);const [newItem, setNewItem] = useState('');const addItem = () => {setItems([...items, newItem]);setNewItem('');};return (<div><ul>{items.map((item, index) => (<ListItem key={index} item={item} />))}</ul><inputvalue={newItem}onChange={(e) => setNewItem(e.target.value)}placeholder="Add new item"/><button onClick={addItem}>Add Item</button></div>);
}export default ParentComponent;

4.?虛擬列表優化長列表渲染

問題:渲染大量列表項會導致頁面卡頓。

解決方案:使用虛擬滾動技術(如 react-window、@tanstack/react-virtual)只渲染當前視口中的元素。

示例場景:渲染大量數據時使用虛擬列表優化性能。

import React from 'react';
import { FixedSizeList as List } from 'react-window';const Row = ({ index, style }) => (<div style={style}>Row {index}</div>
);function VirtualizedList() {return (<Listheight={150}itemCount={1000}itemSize={35}width={300}>{Row}</List>);
}export default VirtualizedList;

虛擬滾動的原理如下:

4.1. IntersectionObserver 接口簡介

IntersectionObserver 接口是 Intersection Observer API 的一部分,用于異步觀察目標元素與其祖先元素或頂級文檔視口之間的交叉狀態,這里的祖先元素或視口被稱為根。

創建 IntersectionObserver 對象時,可以設置監聽的可見區域比例。該配置一旦確定便不可更改,因此一個觀察者對象只能用于監聽指定的變化值。不過,同一個觀察者可以監聽多個目標元素。

// https://caniuse.com/?search=Intersection%20Observer// 創建一個 IntersectionObserver 實例
var observer = new IntersectionObserver(callback, options);

4.2.?IntersectionObserver 支持的參數

4.2.1.??回調函數

當目標元素的可見性發生變化時,觀察器會調用回調函數 callback 。通常,該函數會觸發兩次:一次是元素進入視口,開始可見;另一次是離開視口,開始不可見。

示例代碼如下:

// 初始化一個 IntersectionObserver 實例
var observer = new IntersectionObserver(changes => {for (const change of changes) {console.log(change.time); // 狀態變化的時間戳console.log(change.rootBounds); // 根元素的矩形區域信息console.log(change.boundingClientRect); // 目標元素的矩形區域信息console.log(change.intersectionRect); // 交叉區域的信息console.log(change.intersectionRatio); // 目標元素的可見比例console.log(change.target); // 被觀察的目標元素}
}, {});// 開始監聽目標元素
observer.observe(target);// 停止監聽目標元素
observer.unobserve(target);// 停止所有監聽
observer.disconnect();

在上述代碼中,callback 是一個箭頭函數,接收一個包含 IntersectionObserverEntry 對象的數組作為參數。每個對象代表一個可見性變化的條目。

IntersectionObserverEntry 對象包含以下屬性:

  • time:可見性變化發生的時間戳。

  • target:被觀察的目標元素。

  • rootBounds:根元素的矩形區域信息。

  • boundingClientRect:目標元素的矩形區域信息。

  • intersectionRect:目標元素與視口或根元素的交叉區域信息。

  • intersectionRatio:目標元素的可見比例,完全可見時為1,完全不可見時為0或更小。

4.2.2.??配置對象

IntersectionObserver 構造函數的第二個參數是配置對象,其中可設置以下屬性:

1. threshold 屬性

threshold 決定了在何種交叉比例下觸發回調函數。默認值為 [0],表示當交叉比例達到 0 時觸發回調。用戶可自定義該數組,如 [0, 0.25, 0.5, 0.75, 1] 表示當目標元素可見比例為 0%、25%、50%、75%、100% 時觸發回調。

new IntersectionObserver(entries => { /* ... */ }, { threshold: [0, 0.25, 0.5, 0.75, 1] });

2.?root 屬性和 rootMargin 屬性

root 屬性指定目標元素所在的滾動容器。rootMargin 用于擴展或縮小根元素的邊界,從而影響交叉區域的大小。該屬性使用 CSS 語法定義,如 10px 20px 30px 40px,表示四個方向的 margin 值。

var opts = {root: document.querySelector('.container'),rootMargin: "500px 0px"
};var observer = new IntersectionObserver(callback, opts);

4.3. 應用實例

?4.3.1. 懶加載

使用 IntersectionObserver API 可以實現惰性加載,在目標元素進入視口時才加載,從而節省帶寬。

function query(selector) {return Array.from(document.querySelectorAll(selector));
}var observer = new IntersectionObserver(function(changes) {changes.forEach(function(change) {var container = change.target;var content = container.querySelector('template').content;container.appendChild(content);observer.unobserve(container);});
});query('.lazy-loaded').forEach(function (item) {observer.observe(item);
});

4.3.2.??無限滾動

實現無限滾動的代碼示例如下:

var intersectionObserver = new IntersectionObserver(function (entries) {if (entries[0].intersectionRatio <= 0) return;loadItems(10);console.log('Loaded new items');
});intersectionObserver.observe(document.querySelector('.scrollerFooter'));

4.4.?核心 Hooks 實現

以下是 IntersectionObserver 的核心 Hooks 實現代碼:

import { RefObject, useEffect, useState } from 'react';interface Args extends IntersectionObserverInit {freezeOnceVisible?: boolean;
}export const useIntersectionObserver = (elementRef: RefObject<Element>,{ threshold = 0, root = null, rootMargin = '0%', freezeOnceVisible = false }: Args,
): IntersectionObserverEntry | undefined => {const [entry, setEntry] = useState<IntersectionObserverEntry>();const frozen = entry?.isIntersecting && freezeOnceVisible;const updateEntry = ([entry]: IntersectionObserverEntry[]): void => {setEntry(entry);};useEffect(() => {const node = elementRef?.current;const hasIOSSupport = !!window.IntersectionObserver;if (!hasIOSSupport || frozen || !node) return;const observer = new IntersectionObserver(updateEntry, { threshold, root, rootMargin });observer.observe(node);return () => observer.disconnect();}, [elementRef?.current, JSON.stringify(threshold), root, rootMargin, frozen]);return entry;
};

5.?懶加載組件

問題:頁面首次加載時間過長。

解決方案:使用 React.lazy 和 Suspense,按需加載組件,減少首屏渲染時間。

示例場景:按需加載不常用的組件。

import React, { Suspense } from 'react';const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense>);
}export default App;

6.?優化資源加載

場景:減少初始資源加載,提升頁面性能。

圖片懶加載:

<!-- 圖片懶加載 -->
<img src="example.jpg" loading="lazy" alt="Lazy loaded image" />

代碼分割示例:

// 代碼分割示例
import { lazy } from 'react';const SomeComponent = lazy(() => import('./SomeComponent'));

7.?避免不必要的狀態更新

問題:頻繁更新狀態會導致組件重復渲染。

解決方案:優化狀態管理邏輯,使用 useReducer 或 Context。

示例場景:使用 useReducer 管理復雜狀態。

import React, { useReducer } from 'react';function reducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };default:return state;}
}function Counter() {const [state, dispatch] = useReducer(reducer, { count: 0 });return (<div><button onClick={() => dispatch({ type: 'increment' })}>Count: {state.count}</button></div>);
}export default Counter;

8.?使用 Profiler 分析性能瓶頸

場景:使用 React Profiler 工具來分析和優化組件渲染。

import React, { Profiler } from 'react';function onRenderCallback(id,phase,actualDuration,baseDuration,startTime,commitTime,interactions
) {console.log({ id, phase, actualDuration });
}function App() {return (<Profiler id="App" onRender={onRenderCallback}><MyComponent /></Profiler>);
}export default App;

這些優化手段在實際項目中,可以根據具體情況選擇和組合使用,以達到最佳的性能效果。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92512.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92512.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92512.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

變頻器實習DAY12

目錄變頻器實習DAY12一、繼續&#xff0c;柔性平臺測試&#xff01;上午 王工Modbus新功能測試下午 柔性平臺繼續按照說明書再測一遍附加的小知識點中國貍花貓.git文件附學習參考網址歡迎大家有問題評論交流 (* ^ ω ^)變頻器實習DAY12 一、繼續&#xff0c;柔性平臺測試&…

Redis--多路復用

&#x1f9e9; 一、什么是“客戶端連接”&#xff1f;所謂 客戶端連接 Redis&#xff0c;指的是&#xff1a;一個程序&#xff08;客戶端&#xff09;通過網絡連接到 Redis 服務端&#xff08;比如 127.0.0.1:6379&#xff09;&#xff0c;建立一個 TCP 連接&#xff0c;雙方可…

數組——初識數據結構

一維數組數組的創建數組是一種相同類型元素的集合數組的創建方式C99 中引入了變長數組的概念&#xff0c;變長數組支持數組的大小使用變量來指定明顯這里的vs2019不支持變長數組數組初始化和不完全初始化第二個數組就是典型的不完全初始化&#xff0c;開辟了10個空間&#xff0…

技術速遞|使用 Semantic Kernel 與 A2A 協議構建多智能體解決方案

作者&#xff1a;盧建暉 - 微軟高級云技術布道師 翻譯/排版&#xff1a;Alan Wang 在快速發展的 AI 應用開發領域&#xff0c;能夠協調多個智能體已成為構建復雜企業級解決方案的關鍵。雖然單個 AI 智能體擅長特定任務&#xff0c;但復雜的業務場景往往需要跨平臺、跨框架甚至跨…

前端跨域請求原理及實踐

在前端開發中&#xff0c;"跨域"是一個繞不開的話題。當我們的頁面嘗試從一個域名請求另一個域名的資源時&#xff0c;瀏覽器往往會拋出類似Access to fetch at xxx from origin xxx has been blocked by CORS policy的錯誤。下面將深入探討跨域請求的底層原理&#…

SpringBoot07-數據層的解決方案:SQL

一、內置數據源 1-1、【回顧】Druid數據源的配置 druid的兩種導入格式 1-2、springboot提供的3種內置數據源的配置 若是不配置Druid&#xff0c; springboot提供了3中默認的數據源配置&#xff0c;它們分別是&#xff1a; 1. HikariCP&#xff08;默認&#xff09; 從 Spring…

前端自動化埋點:頁面模塊級行為跟蹤與問題定位系統??的技術設計方案

一、核心設計目標??精細化監控??&#xff1a;定位到頁面中??單個模塊??的曝光、點擊等行為。??低侵入性??&#xff1a;業務代碼與埋點邏輯解耦&#xff0c;降低開發維護成本。??鏈路可追蹤??&#xff1a;串聯用戶從曝光到操作的完整行為路徑。??實時性??&a…

Node.js 與 Java 性能對比

一、核心架構與任務模型對比Node.js 單線程事件循環 非阻塞I/O 通過V8引擎執行JavaScript&#xff0c;采用事件驅動模型&#xff0c;所有I/O操作&#xff08;如網絡請求、文件讀寫&#xff09;均為非阻塞。單線程處理所有請求&#xff0c;但通過事件循環&#xff08;Event Loo…

Python3常見接口函數

Python3常見接口函數一、基礎內置函數 輸入輸出 print()&#xff1a;輸出內容input()&#xff1a;讀取用戶輸入 類型轉換 int()、float()、str()、bool()&#xff1a;基礎類型轉換list()、tuple()、set()、dict()&#xff1a;容器類型轉換bin()、hex()、oct()&#xff1a;進制轉…

《P4092 [HEOI2016/TJOI2016] 樹》

題目描述在 2016 年&#xff0c;佳媛姐姐剛剛學習了樹&#xff0c;非常開心。現在他想解決這樣一個問題&#xff1a;給定一顆有根樹&#xff0c;根為 1 &#xff0c;有以下兩種操作&#xff1a;標記操作&#xff1a;對某個結點打上標記。&#xff08;在最開始&#xff0c;只有結…

TCP頭部

TCP頭部字段詳解1. 源端口和目的端口&#xff08;各16位&#xff09;功能&#xff1a;標識發送和接收應用程序范圍&#xff1a;0-65535&#xff08;0-1023為知名端口&#xff09;技術細節&#xff1a;客戶端通常使用臨時端口&#xff08;1024-65535&#xff09;服務端使用固定端…

LinkedList與鏈表(單向)(Java實現)

引入鏈表結構&#xff1a;在ArrayList任意位置插入或者刪除元素時&#xff0c;就需要將后序元素整體往前或者往后 搬移&#xff0c;時間復雜度為O(n)&#xff0c;效率比較低&#xff0c;因此ArrayList不適合做任意位置插入和刪除比較多的場景。因此&#xff1a;java集合中又引入…

網絡--VLAN技術

目錄 VLAN實驗報告 一、實驗拓撲 二、實驗要求 三、實驗思路 1、實驗準備 2. VLAN 3. DHCP 自動分配 4、 全網可達驗證 四、實驗步驟 &#xff08;一&#xff09;交換機配置- VLAN 創建與接口劃分 &#xff08;二&#xff09;路由器配置&#xff08;R1&#xff0c…

網絡基礎17--設備虛擬化

一、傳統MSTPVRRP的不足傳統MSTPVRRP設計&#xff1a;規劃復雜&#xff1a;需要詳細規劃VRRP多實例的Master歸屬、MSTP的VLAN和生成樹實例歸屬&#xff0c;以及IP網段。收斂速度慢&#xff1a;故障恢復速度一般在秒級&#xff0c;VRRP收斂時間至少需要3秒&#xff0c;故障恢復速…

深入解析Hadoop資源隔離機制:Cgroups、容器限制與OOM Killer防御策略

Hadoop資源隔離機制概述在分布式計算環境中&#xff0c;資源隔離是保障多任務并行執行穩定性的關鍵技術。Hadoop作為主流的大數據處理框架&#xff0c;其資源管理能力直接影響集群的吞吐量和任務成功率。隨著YARN架構的引入&#xff0c;Hadoop實現了計算資源與存儲資源的解耦&a…

static 關鍵字的 特殊性

static 關鍵字的 “特殊性” 主要體現在其與類、對象的綁定關系&#xff0c;以及由此帶來的一些反常規規則&#xff0c;具體如下&#xff1a;生命周期與內存位置特殊靜態成員&#xff08;變量 / 方法&#xff09;隨類加載而創建&#xff0c;隨類卸載而銷毀&#xff0c;生命周期…

win10系統Apache以 FastCGI方式運行PHP

文件下載及官方網站 VC運行庫Latest下載頁:Latest supported Visual C Redistributable downloads | Microsoft Learnapache httpd官網:Welcome! - The Apache HTTP Server Project下載頁:Apache VS17 binaries and modules downloadphp官網:PHP: Hypertext Preprocessor下載頁…

MCP與企業數據集成:ERP、CRM、數據倉庫的統一接入

MCP與企業數據集成&#xff1a;ERP、CRM、數據倉庫的統一接入 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般絢爛的技術棧中&#xff0c;我是那個永不停歇的色彩收集者。 &#x1f98b; 每一個優化都是我培育的花朵&#xff0c;每一個特性都是我…

【milvus檢索】milvus檢索召回率

Milvus中兩種核心查詢方式&#xff1a;暴力搜索&#xff08;Brute-force Search&#xff09; 和 近似最近鄰搜索&#xff08;Approximate Nearest Neighbor, ANN&#xff09;。 逐一計算相似度&#xff1a;這是暴力搜索&#xff0c;能保證100%找到最相似的向量&#xff0c;但速…

docker Neo4j

Day 1 &#xff1a;Docker Desktop 基礎熟悉 運行官方 hello-world 測試&#xff1a; docker -run hello-world 運行 Nginx 體驗容器暴露端口&#xff1a; docker run -d -p 8080:80 nginx -d --detach 以 分離模式 運行容器 -p --publish 設置 宿主機與容器的端口映射。…