前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >性能:React 实战优化技巧

性能:React 实战优化技巧

作者头像
奋飛
发布2024-05-25 19:36:35
510
发布2024-05-25 19:36:35
举报
文章被收录于专栏:Super 前端Super 前端

🌿 性能优化的主要点: 1️⃣ 减少 DOM 的渲染频次 2️⃣ 减少 DOM 的渲染范围 3️⃣ 非必要的内容延后处理

React 在组件触发刷新的时候,会深度遍历所有子组件。➡️ 父组件刷新,子组件跟着刷新。

避免不必要的组件重新渲染,是提高程序性能的重要方式之一。

缓存方式
React.memo
代码语言:javascript
复制
const MemoizedComponent = memo(SomeComponent, arePropsEqual?)

使用 memo 将组件包装起来,以获得该组件的一个 记忆化 版本。通常情况下,只要该组件的 props 没有改变,这个记忆化版本就不会在其父组件重新渲染时重新渲染。

即使一个组件被记忆化了,当它自身的状态/ context 发生变化时,它仍然会重新渲染。memoization 只与从父组件传递给组件的 props 有关。

代码语言:javascript
复制
const MyComponent = memo(({name}) => {
    const [age, setAge] = useState();
    return (<>
        <input type="text" value={age} onChange={(e) => setAge(e.target.value)}/>
				{Date.now()}
    </>)
});

props name 改变,及 ② 自身 state age 改变,组件都会重新渲染。

当使用 memo 时,只要任何一个 prop 与先前的值不等的话,组件就会重新渲染。这意味着 React 会使用 Object.is 比较组件中的每个 prop 与其先前的值。

代码语言:javascript
复制
Object.is(3, 3) 		// true
Object.is({}, {})		// false

Object.is()==运算符并不等价== 运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型)。

代码语言:javascript
复制
"" == false			     // true
Object.is("", false)     // false

Object.is()=== 运算符也不等价Object.is()=== 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。

代码语言:javascript
复制
+0 === -0			 // true
0 === NaN		     // false
Object.is(-0, +0) 	 // false
Object.is(0, NaN)    // false

当props是对象或函数时,Object.is() 永远不相等。为了解决这个问题,React 中引入了 useMemouseCallback


👉 👉 如果 props 是一个对象,可以使用 useMemo 避免父组件每次都重新创建该对象。

useMemo
代码语言:javascript
复制
const cachedValue = useMemo(calculateValue, dependencies)

在每次重新渲染的时候能够缓存计算的结果。如果依赖项没有发生改变,它将返回上次缓存的值;否则将再次调用 calculateValue,并返回最新结果。

代码语言:javascript
复制
function Page() {
  const [name, setName] = useState('ligang');
  const [age, setAge] = useState(34);

  // 避免因 `Object.is` 不相等,导致的重新渲染
  const person = useMemo(
    () => ({ name, age }),
    [name, age]
  );

  return <MyComponent person={person} />;
}

const MyComponent = memo(({person}) => {
  // ...
});

👉👉 如果 props 是一个函数,可以将函数包装到 useCallback 避免父组件每次都重新创建该函数。

useCallback
代码语言:javascript
复制
const cachedFn = useCallback(fn, dependencies)

在多次渲染中缓存函数。在初次渲染时,useCallback 返回传入的 fn 函数;在之后的渲染中,如果依赖没有改变,useCallback 返回上一次渲染中缓存的 fn 函数;否则返回这一次渲染传入的 fn

代码语言:javascript
复制
export default () => {
    const [name, setName] = useState('');
  
  	// 不依赖任何state
    const handleSubmit = useCallback(() => {
        // ...
    }, []);
  
    return MyComponent onSubmit={handleSubmit}/>;
}

const MyComponent = memo(({onSubmit}) => {
  // ...
});

【示例】

不使用 useCallback,每次父组件 name 改变,子组件MyComponent 都重新渲染(即便使用 memo 进行了包裹)

使用 useCallback,每次父组件 name 改变,子组件MyComponent 不再重新渲染( memo 生效)

精准控制
key

key 是一个特殊的字符串属性,用于帮助 React 识别哪些元素改变了。

在列表渲染时 key 属性可以用于识别 React 的 diff 算法哪些列表项已更改,通过复用具有相同 key 的组件实例,React可以减少了不必要的DOM操作&重新渲染,从而提升界面更新的效率。

为了正确使用key属性,确保所提供的key是稳定、唯一且可预测的。

虚拟化

最常见的虚拟列表。仅渲染可见部分,而不是全部内容,实现性能的提升。

以 ahooks useVirtualList 为例:

代码语言:javascript
复制
export default () => {
  const containerRef = useRef(null);
  const wrapperRef = useRef(null);

  const originalList = useMemo(() => Array.from(Array(99999).keys()), []);

  // 配置虚拟滚动
  const [list] = useVirtualList(originalList, {
    containerTarget: containerRef, 	 // 外面容器
    wrapperTarget: wrapperRef,		 // 内部容器
    itemHeight: 60,					 // 行高度
    overscan: 10,  					 // 视区上、下额外展示的 DOM 节点数量
  });
  return (
    <>
      {/* 指定可视区域高度 */}
      <div ref={containerRef} style={{ height: '300px', overflow: 'auto', border: '1px solid' }}>
        <div ref={wrapperRef}>
          {list.map((ele) => (
            <div
              style={{
                height: 52,
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                border: '1px solid #e8e8e8',
                marginBottom: 8,
              }}
              key={ele.index}
            >
              Row: {ele.data}
            </div>
          ))}
        </div>
      </div>
    </>
  );
};
代码分割&懒加载

代码分隔(Code splitting)是一种前端技术,用于将应用程序的代码拆分成较小的块,以优化加载性能和减少初始加载时间。它可以帮助减少初始下载量,并根据需要动态加载所需的代码。

在需要时才加载组件,从而减少初始加载时间并提高性能。

动态导入(Dynamic Imports)
代码语言:javascript
复制
export default () => {
  const [module, setModule] = useState(null);
  
  useEffect(() => {
    import('./MyModule').then((mod) => setModule(mod));
  }, []);

  return module ? <module.default /> : <div>Loading...</div>;
}
React.lazy()Suspense
代码语言:javascript
复制
const MyModule = React.lazy(() => import('./MyModule'));

export default () => (
  <Suspense fallback={<div>Loading...</div>}>
    <MyModule />
  </Suspense>
);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 缓存方式
    • React.memo
      • useMemo
        • useCallback
        • 精准控制
          • key
            • 虚拟化
            • 代码分割&懒加载
              • 动态导入(Dynamic Imports)
                • React.lazy()和 Suspense
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档