React性能优化的8种方式了解一下

2023-10-31

react凭借virtual DOM和diff算法拥有高效的性能,除此之外也有很多其他的方法和技巧可以进一步提升react性能,在本文中我将列举出可有效提升react性能的几种方法,帮助我们改进react代码,提升性能。但是我们不必一定要在项目中使用这些方法,但是我们有必要知道如何使用这些方法。

使用React.Memo来缓存组件

提升应用程序性能的一种方法是实现memoization。Memoization是一种优化技术,主要通过存储昂贵的函数调用的结果,并在再次发生相同的输入时返回缓存的结果,以此来加速程序。
父组件的每次状态更新,都会导致子组件重新渲染,即使传入子组件的状态没有变化,为了减少重复渲染,我们可以使用React.memo来缓存组件,这样只有当传入组件的状态值发生变化时才会重新渲染。如果传入相同的值,则返回缓存的组件。示例如下:

export default React.memo((props) => {
  return (
    <div>{props.value}</div>  
  )
});
复制代码

使用useMemo缓存大量的计算

有时渲染是不可避免的,但如果您的组件是一个功能组件,重新渲染会导致每次都调用大型计算函数,这是非常消耗性能的,我们可以使用新的useMemo钩子来“记忆”这个计算函数的计算结果。这样只有传入的参数发生变化后,该计算函数才会重新调用计算新的结果。
通过这种方式,您可以使用从先前渲染计算的结果来挽救昂贵的计算耗时。总体目标是减少JavaScript在呈现组件期间必须执行的工作量,以便主线程被阻塞的时间更短。

// 避免这样做
function Component(props) {
  const someProp = heavyCalculation(props.item);
  return <AnotherComponent someProp={someProp} /> 
}
  
// 只有 `props.item` 改变时someProp的值才会被重新计算
function Component(props) {
  const someProp = useMemo(() => heavyCalculation(props.item), [props.item]);
  return <AnotherComponent someProp={someProp} /> 
}
复制代码

使用React.PureComponent , shouldComponentUpdate

父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同props。但是这里的重新渲染不是说会更新DOM,而是每次都会调用diif算法来判断是否需要更新DOM。这对于大型组件例如组件树来说是非常消耗性能的。
在这里我们就可以使用React.PureComponent , shouldComponentUpdate生命周期来确保只有当组件props状态改变时才会重新渲染。如下例子:

export default function ParentComponent(props) {
  return (
    <div>
      <SomeComponent someProp={props.somePropValue}
    <div>
      <AnotherComponent someOtherProp={props.someOtherPropValue} />
    </div>
   </div>
 )
}

export default function SomeComponent(props) {
  return (
    <div>{props.someProp}</div>  
  )
}

// 只要props.somePropValue 发生变化,不论props.someOtherPropValue是否发生变化该组件都会发生变化
export default function AnotherComponent(props) {
  return (
    <div>{props.someOtherProp}</div>  
  )
}
复制代码

我们可以使用React.PureComponent 或shouldComponentUpdate 进行如下优化:

// 第一种优化
class AnotherComponent extends React.PureComponent {
  render() {
    return <div>{this.props.someOtherProp}</div>   
  }
}

//第二种优化
class AnotherComponent extends Component {
  shouldComponentUpdate(nextProps) {
    return this.props !== nextProps
  }
  render() {
    return <div>{this.props.someOtherProp}</div>   
  }
}
复制代码


PureComponent会进行浅比较来判断组件是否应该重新渲染,对于传入的基本类型props,只要值相同,浅比较就会认为相同,对于传入的引用类型props,浅比较只会认为传入的props是不是同一个引用,如果不是,哪怕这两个对象中的内容完全一样,也会被认为是不同的props。
需要注意的是在对于那些可以忽略渲染时间的组件或者是状态一直变化的组件则要谨慎使用PureComponent,因为进行浅比较也会花费时间,这种优化更适用于大型的展示组件上。大型组件也可以拆分成多个小组件,并使用memo来包裹小组件,也可以提升性能。

避免使用内联对象

使用内联对象时,react会在每次渲染时重新创建对此对象的引用,这会导致接收此对象的组件将其视为不同的对象,因此,该组件对于prop的浅层比较始终返回false,导致组件一直重新渲染。
许多人使用的内联样式的间接引用,就会使组件重新渲染,可能会导致性能问题。为了解决这个问题,我们可以保证该对象只初始化一次,指向相同引用。另外一种情况是传递一个对象,同样会在渲染时创建不同的引用,也有可能导致性能问题,我们可以利用ES6扩展运算符将传递的对象解构。这样组件接收到的便是基本类型的props,组件通过浅层比较发现接受的prop没有变化,则不会重新渲染。示例如下:

// Don't do this!
function Component(props) {
  const aProp = { someProp: 'someValue' }
  return <AnotherComponent style={{ margin: 0 }} aProp={aProp} />  
}

// Do this instead :)
const styles = { margin: 0 };
function Component(props) {
  const aProp = { someProp: 'someValue' }
  return <AnotherComponent style={styles} {...aProp} />  
}
复制代码

避免使用匿名函数

虽然匿名函数是传递函数的好方法(特别是需要用另一个prop作为参数调用的函数),但它们在每次渲染上都有不同的引用。这类似于上面描述的内联对象。为了保持对作为prop传递给React组件的函数的相同引用,您可以将其声明为类方法(如果您使用的是基于类的组件)或使用useCallback钩子来帮助您保持相同的引用(如果您使用功能组件)。
当然,有时内联匿名函数是最简单的方法,实际上并不会导致应用程序出现性能问题。这可能是因为在一个非常“轻量级”的组件上使用它,或者因为父组件实际上必须在每次props更改时重新渲染其所有内容。因此不用关心该函数是否是不同的引用,因为无论如何,组件都会重新渲染。

// 避免这样做
function Component(props) {
  return <AnotherComponent onChange={() => props.callback(props.id)} />  
}

// 优化方法一
function Component(props) {
  const handleChange = useCallback(() => props.callback(props.id), [props.id]);
  return <AnotherComponent onChange={handleChange} />  
}

// 优化方法二
class Component extends React.Component {
  handleChange = () => {
   this.props.callback(this.props.id) 
  }
  render() {
    return <AnotherComponent onChange={this.handleChange} />
  }
}
复制代码

延迟加载不是立即需要的组件

延迟加载实际上不可见(或不是立即需要)的组件,React加载的组件越少,加载组件的速度就越快。因此,如果您的初始渲染感觉相当粗糙,则可以在初始安装完成后通过在需要时加载组件来减少加载的组件数量。同时,这将允许用户更快地加载您的平台/应用程序。最后,通过拆分初始渲染,您将JS工作负载拆分为较小的任务,这将为您的页面提供响应的时间。这可以使用新的React.Lazy和React.Suspense轻松完成。

// 延迟加载不是立即需要的组件
const MUITooltip = React.lazy(() => import('@material-ui/core/Tooltip'));
function Tooltip({ children, title }) {
  return (
    <React.Suspense fallback={children}>
      <MUITooltip title={title}>
        {children}
      </MUITooltip>
    </React.Suspense>
  );
}

function Component(props) {
  return (
    <Tooltip title={props.title}>
      <AnotherComponent />
    </Tooltip>
  )
}
复制代码

调整CSS而不是强制组件加载和卸载

渲染成本很高,尤其是在需要更改DOM时。每当你有某种手风琴或标签功能,例如想要一次只能看到一个项目时,你可能想要卸载不可见的组件,并在它变得可见时将其重新加载。如果加载/卸载的组件“很重”,则此操作可能非常消耗性能并可能导致延迟。在这些情况下,最好通过CSS隐藏它,同时将内容保存到DOM。前端培训
尽管这种方法并不是万能的,因为安装这些组件可能会导致问题(即组件与窗口上的无限分页竞争),但我们应该选择在不是这种情况下使用调整CSS的方法。另外一点,将不透明度调整为0对浏览器的成本消耗几乎为0(因为它不会导致重排),并且应尽可能优先于更该visibility 和 display。
有时在保持组件加载的同时通过CSS隐藏可能是有益的,而不是通过卸载来隐藏。对于具有显著的加载/卸载时序的重型组件而言,这是有效的性能优化手段。

// 避免对大型的组件频繁对加载和卸载
function Component(props) {
  const [view, setView] = useState('view1');
  return view === 'view1' ? <SomeComponent /> : <AnotherComponent />  
}

// 使用该方式提升性能和速度
const visibleStyles = { opacity: 1 };
const hiddenStyles = { opacity: 0 };
function Component(props) {
  const [view, setView] = useState('view1');
  return (
    <React.Fragment>
      <SomeComponent style={view === 'view1' ? visibleStyles : hiddenStyles}>
      <AnotherComponent style={view !== 'view1' ? visibleStyles : hiddenStyles}>
    </React.Fragment>
  )
}
复制代码

使用React.Fragment避免添加额外的DOM

有些情况下,我们需要在组件中返回多个元素,例如下面的元素,但是在react规定组件中必须有一个父元素。

            <h1>Hello world!</h1>
            <h1>Hello there!</h1>
            <h1>Hello there again!</h1>
复制代码

因此你可能会这样做,但是这样做的话即使一切正常,也会创建额外的不必要的div。这会导致整个应用程序内创建许多无用的元素:

function Component() {
        return (
            <div>
                <h1>Hello world!</h1>
                <h1>Hello there!</h1>
                <h1>Hello there again!</h1>
            </div>
        )
}
复制代码

实际上页面上的元素越多,加载所需的时间就越多。为了减少不必要的加载时间,我们可以使React.Fragment来避免创建不必要的元素。

function Component() {
        return (
            <React.Fragment>
                <h1>Hello world!</h1>
                <h1>Hello there!</h1>
                <h1>Hello there again!</h1>
            </React.Fragment>
        )
}
复制代码

总结

我们文中列出的基本上是React内部提供的性能优化方法,这些方法可以帮助React更好地执行,并没有列出例如Immutable.js第三方工具库的优化方法。其实性能优化的方法有很多,但正如上面所说的,合适的方法也要在合适的场景下使用,过度的使用性能优化反而会得不偿失。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React性能优化的8种方式了解一下 的相关文章

随机推荐

  • mybatis条件判断/ 动态sql

    1 if标签 单条件判断 判断完第一个条件 对下一个条件进行判断 看是否能满足条件 满足则执行
  • 开发中用到的数据库查询案例

    目录 1 查询过去12个月的数据 并统计每个月数据的数量 2 查询过去12个月的数据 并统计每个月数据的数量 如果某个月数据没有 也展示出为0 1 查询过去12个月的数据 并统计每个月数据的数量 select date format cas
  • Qt入门-界面多语言国际化的实现

    Qt为国际化的实现提供了简便的方法 下面使用Qt Linguist示例一个中文语言界面的生成 我使用以前的实例 http blog csdn net xgbing article details 7778856 它是一个英文界面 步骤如下
  • 什么场景该用 MongoDB

    摘要 前段时间 MongoDB源码团队 在云栖社区上发起了一个 MongoDB 使用场景及运维管理问题交流探讨 的技术话题 有近五千人关注了该话题讨论 很多人比较关心 MongoDB的适用场景 也有用户在话题里分享了自己的业务场景 这里就
  • 【题解】poj2689(LibreOJ10197) 线性筛

    题目链接 筛出2到sqrt u 的所有质数 再标记 l u 中是质数p倍数的数 最后枚举相邻质数 部分代码实现参考了大佬题解 题目描述 给定两个整数 L R L R L R 求闭区间 L
  • Linux I2C设备驱动基本规范

    不同于单片机驱动开发 即使是简单的I2C设备驱动程序 如果要在Linux上实现同种功能的驱动程序 事情也会变的复杂起来 对于初学者而言 主要的困难就是不知道如何使用Linux现有的驱动框架 去完成驱动程序的开发 I2C设备驱动 相对来说比较
  • VUE+element-ui微信充值支付

    VUE element ui微信充值支付 前端页面是参考别人的 前端页面是参考别人的 链接 http www lanlanwork com blog post 5839
  • docker build 命令后 . 号的意思

    点击在我的博客 xuxusheng com 中查看 有更好的排版哦 参考文章 https yeasy gitbooks io docker practice content image build html 我们在使用 docker bui
  • JS-计算日期差值;计算日期之间的月数

    计算两天之间的日期差值 输入格式 yyyy MM DD function daysBetween sDate1 sDate2 Date parse 解析一个日期时间字符串 并返回1970 1 1 午夜距离该日期时间的毫秒数 var time
  • MATLAB 中的非线性回归:将复杂模型拟合到数据

    线回归是一种强大的技术 用于将复杂模型拟合到不遵循线性关系的数据 MATLAB 提供了强大的工具来执行非线性回归分析 使您能够找到最适合模型的参数并根据拟合模型进行预测 在本教程中 我们将介绍使用 MATLAB 环境中可用的数据集在 MAT
  • window安装Linux

    一 安装VMware 可通过VMware官网VMware Delivering a Digital Foundation For Businesses下载正版VMware 下载完成后直接打开 或使用管理员身份运行 点击是 点击下一步 点击我
  • 最简单的网页登录注册功能讲解及其实现

    目录 前言 结构 创建数据库存储账户信息 html注册页面 注册处理后台程序 html登录页面 后台登录处理程序 演示 前言 最近打算做一个网页登录的练习 今天撸一个最简单的demo 记录一下学习的过程 结构 实现一个网页最基本的注册与登录
  • SpringSecurity系列 之 集成第三方登录(包括默认的用户名密码、短信验证码和github三种登录方式)

    一 前言 前面在 SpringSecurity系列 之 认证过程和原理 一文中 我们已经学习了SpringSecurity的认证过程 实现过程如下图所示 根据这个认证过程 我们如何实现集成多种第三方登录的方案呢 我们这里提供了一种思路 首先
  • (若依)RuoYi后台管理框架前端

    若依后台管理 官方网址 后台管理二开推荐 官网的源码地址可供下载前后端代码 最近进到公司后跟着团队接了两家公司的App一套的开发 到公司的第一天就是先配置开发环境 安装软件之类的 第二天 带着熟悉RuoYi框架 很巧带我的大哥和我是一个学校
  • 关于一些奇葩的兼容bug

    iPhoneXr canvas层级失效 大致就是这样 一个img在canvas上面 所有手机img正常显示 就是ios的这个机型有问题 系统版本14 0 img的层级不论多大都会被canvas覆盖 解决办法尽然是将canvas的层级设置为
  • Ubuntu下FastDFS环境的安装搭建

    Ubuntu下FastDFS环境的安装搭建 一 FastDFS简介 FastDFS是一个个人开发的 轻量级的 开源的分布式文件系统 纯C语言编写 其功能包括 文件存储 文件同步 文件访问 文件上传 文件下载 等 解决了大容量存储和负载均衡的
  • Qt主线程卡死,竟然与X11 的 _XReply() 有关

    在ubuntu系统下运行Qt程序 Qt的程序就相当于X11的一个客户端 受X11的限制 近期 在处理一起离奇事件 发现UI的qt界面已经卡死了 无鼠标键盘任何反应 但程序是运行的 比如与其他设备通信是正常的 说明程序本身没有死掉 通过断点
  • 中移链Java-SDK实战使用

    id BSN 2021 公众号 BSN研习社 作者 中移信息 中移链是基于EOS区块链框架改造 满足BSN开放联盟链要求 符合国内监管政策 BSN开放联盟链 BSN Open Permissioned Blockchain 简称OPB 包括
  • ★教程1:matlab学习教程入门100例目录

    1 订阅本教程用户可以免费获得本博任意2个 包括所有免费专栏和付费专栏 博文对应代码 私信博主给出代码博文的链接和邮箱 2 本MATLAB课程的所有案例 部分理论知识点除外 均由博主编写而成 供有兴趣的朋友们自己订阅学习使用 未经本人允许
  • React性能优化的8种方式了解一下

    react凭借virtual DOM和diff算法拥有高效的性能 除此之外也有很多其他的方法和技巧可以进一步提升react性能 在本文中我将列举出可有效提升react性能的几种方法 帮助我们改进react代码 提升性能 但是我们不必一定要在