网站首页 > 开源技术 正文
前言
性能优化是软件开发中永远不会过时的话题,本篇将介绍在React编码过程中需要注意的性能优化点。鉴于图片懒加载、虚拟滚动列表等已成为广为人知的通用性能优化手段,本文将不再赘述这些内容。
正文
1.memo
memo允许组件在 props 没有改变的情况下跳过重新渲染
默认通过Object.is比较每个prop,可通过第二个参数,传入自定义函数来控制对比过程
const Chart = memo(function Chart({ dataPoints }) {
// ...
}, arePropsEqual);
function arePropsEqual(oldProps, newProps) {
return (
oldProps.dataPoints.length === newProps.dataPoints.length &&
oldProps.dataPoints.every((oldPoint, index) => {
const newPoint = newProps.dataPoints[index];
return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;
})
);
}
2.useMemo
在每次重新渲染的时候能够缓存计算的结果
import { useState, useMemo } from "react";
function App() {
const [count, setCount] = useState(0);
const memoizedValue = useMemo(() => {
//创建1000位数组
const list = new Array(1000).fill(null).map((_, i) => i);
//对数组求和
const total = list.reduce((res, cur) => (res += cur), 0);
//返回计算的结果
return count + total;
//添加依赖项,只有count改变时,才会重新计算
}, [count]);
return (
<div>
{memoizedValue}
<button onClick={() => setCount((prev) => prev + 1)}>按钮</button>
</div>
);
}
export default App;
3.useMemo
缓存函数的引用地址,仅在依赖项改变时才会更新
import { useState, memo } from 'react';
const App = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((prev) => prev + 1);
};
return (
<div>
{count}
<MyButton handleClick={handleClick} />
</div>
);
};
const MyButton = memo(function MyButton({ handleClick }: { handleClick: () => void }) {
console.log('子组件渲染');
return <button onClick={handleClick}>按钮</button>;
});
export default App;
点击按钮,可以发现即使子组件使用memo包裹了,但还是更新了,控制台打印出“子组件渲染”。这是因为父组件App每次更新时,函数handleClick每次都返回了新的引用地址,因此对于子组件来说每次传入的都是不一样的值,从而触发重渲染。
同样的,减少使用通过内联函数绑定事件。每次父组件更新时,匿名函数都会返回一个新的引用地址,从而触发子组件的重渲染
<MyButton handleClick={() => setCount((prev) => prev + 1)} />
使用useCallback可以缓存函数的引用地址,将handleClick改为
const handleClick = useCallback(()=>{
setCount(prev=>prev+1)
},[])
再点击按钮,会发现子组件不会再重新渲染。
4.useTransition
使用useTransition提供的startTransition来标记一个更新作为不紧急的更新。这段任务可以接受延迟或被打断渲染,进而去优先考虑更重要的任务执行
页面会先显示list2的内容,之后再显示list1的内容
import { useState, useEffect, useTransition } from "react";
const App = () => {
const [list1, setList1] = useState<null[]>([]);
const [list2, setList2] = useState<null[]>([]);
const [isPending, startTransition] = useTransition();
useEffect(() => {
startTransition(() => {
//将状态更新标记为 transition
setList1(new Array(10000).fill(null));
});
}, []);
useEffect(()=>{
setList2(new Array(10000).fill(null));
},[])
return (
<>
{isPending ? "pending" : "nopending"}
{list1.map((_, i) => (
<div key={i}>{i}</div>
))}
-----------------list2
{list2.map((_, i) => (
<div key={i}>6666</div>
))}
</>
);
};
export default App;
5、useDeferredValue
可以让我们延迟渲染不紧急的部分,类似于防抖但没有固定的延迟时间
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}
6、Fragment
当呈现多个元素而不需要额外的容器元素时,使用React.Fragment可以减少DOM节点的数量,从而提高呈现性能
const MyComponent = () => {
return (
<React.Fragment>
<div>Element 1</div>
<div>Element 2</div>
<div>Element 3</div>
</React.Fragment>
);
};
7、合理使用Context
Context 能够在组件树间跨层级数据传递,正因其这一独特机制,Context 可以绕过 React.memo 或 shouldComponentUpdate 设定的比较过程。
也就是说,一旦 Context 的 Value 变动,所有使用 useContext 获取该 Context 的组件会全部 forceUpdate。即使该组件使用了memo,且 Context 更新的部分 Value 与其无关
为了使组件仅在 context 与其相关的value发生更改时重新渲染,将组件分为两个部分。在外层组件中从 context 中读取所需内容,并将其作为 props 传递给使用memo优化的子组件。
8、尽量避免使用index作为key
在渲染元素列表时,尽量避免将数组索引作为组件的key。如果列表项有添加、删除及重新排序的操作,使用index作为key,可能会使节点复用率变低,进而影响性能
使用数据源的id作为key
const MyComponent = () => {
const items = [{ id: 1, name: "Item 1" }, { id: 2, name: "Item 2" }, { id: 3, name: "Item 3" }];
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
9、懒加载
通过React.lazy和React.Suspense实施代码分割策略,将React应用细分为更小的模块,确保在具体需求出现时才按需加载相应的部分
定义路由
import { lazy } from 'react';
import { createBrowserRouter } from 'react-router-dom';
const Login = lazy(() => import('../pages/login'));
const routes = [
{
path: '/login',
element: <Login />,
},
];
//可传第二个参数,配置base路径 { basename: "/app"}
const router = createBrowserRouter(routes);
export default router;
引用路由
import { Suspense } from 'react';
import { RouterProvider } from 'react-router-dom';
import ReactDOM from 'react-dom/client';
import router from './router';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<Suspense fallback={<div>Loading...</div>}>
<RouterProvider router={router} />
</Suspense>,
);
10、组件卸载时的清理
在组件卸载时清理全局监听器、定时器等。防止内存泄漏影响性能
import { useState, useEffect, useRef } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const timer = useRef<NodeJS.Timeout>();
useEffect(() => {
// 定义定时器
timer.current = setInterval(() => {
setCount((count) => count + 1);
}, 1000);
const handleOnResize = () => {
console.log('Window resized');
};
// 定义监听器
window.addEventListener('resize', handleOnResize);
// 在组件卸载时清除定时器和监听器
return () => {
clearInterval(timer.current);
window.removeEventListener('resize', handleOnResize);
};
}, []);
return (
<div>
<p>{count}</p>
</div>
);
}
export default MyComponent;
- 上一篇: Vue和React的一些对比:哪个更适合你?
- 下一篇: 风电专有名词-来自国译翻译公司
猜你喜欢
- 2025-01-03 Vue和React的一些对比:哪个更适合你?
- 2025-01-03 React 的一些最佳安全实践
- 2025-01-03 搭建一个无需构建工具的 React 页面
- 2025-01-03 支持Vue、React混用的veaury库大火?前端摆脱二选一?
- 2025-01-03 前端开发react框架 - 组件
- 2025-01-03 从0到1无比流畅的React入门教程
- 2025-01-03 如何设计更优雅的 React 组件?
- 2025-01-03 React中使用Ant Table组件
- 2025-01-03 React支持了ES6 Classes,听听他们怎么说
- 2025-01-03 React 中的 Canvas 动画
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)