React性能优化实践

序言

时光荏苒,在一家公司从毕业到现在工作了五年;为了寻求突破和自身发展,不得已忍痛提出离职。 感谢领导们的信任和同事们的配合,能够让我在团队中淋漓尽致地发挥所学所长。 在做离职交接的过程中也给大家整理了一下之前工作遇到的React性能问题以及优化方案,希望对大家以后的工作有帮助。

理解React设计

关于React性能优化,脑子里自然会浮现出shouldComponentUpdate React.memo; 但一股脑的使用React提供的优化api,有时候会发现性能也没有任何提升;要真正做到性能优化,得先知道React的更新流程:

  1. 当触发一次setState之后,React会从根节点开始遍历整颗树,对每个节点进行处理。
  2. React有自带性能优化策略,当一个节点的props、context没有变化且没有调用setState,那么该节点会进行复用。
  3. 当前节点还提供了一个childLanes字段,用于判断子孙节点是否存在更新,如果子孙节点没有更新,则会复用整颗子树,遍历会提前结束。
  4. childLanes解释:当一个子组件调用了setState,或者子组件里面使用了context且context的值变化了,那么其祖先节点就会附带上childLanes,至于具体怎么附带上去这里不展开讲。
  • 因此,我们做性能优化的目标,就是要减少每个组件的props、context的变化;让React尽可能地对树进行复用

实践

如何优化props

当一个组件发生了setState更新后:

  1. 会重新生成新的虚拟dom,虚拟dom里包含了props等属性
  2. 传给子组件的props引用也就发生了变化,会导致子组件更新、重新生成虚拟dom
  3. 进而影响子组件的子组件props也发生了变化,一直传染下去,导致整颗树都处理了一遍

这种情况就需要使用React.memo或者shouldComponentUpdate,其原理就是对props里面的每一个属性进行浅比较、而不是直接判断props引用

如何优化Context

当你你把所有组件都加上了memo或shouldComponentUpdate,发现一点用都没有,此时就要思考一下context的设计。

  • 例子:
  1. 在根组件定义了一个context,里面包含了a、b、c三个字段数据
  2. 有三个子组件,A组件使用了a、c两个数据,B组件使用了a、b数据,C组件使用了b、c数据
  3. C是个耗性能的组件,当Context里的a数据变了,C组件还是会重现渲染,即使加了React.memo和shouldComponentUpdate
  4. 由于React有着数据不可变性的原则,修改Context的某一个数据,只能生成一个新的Context,而不能在原来的Context上修改某个字段的数据,所以无论哪一个字段的更新都会导致整个context发生了变化
  5. 解决方案就是把性能消耗大的组件b、c数据单独抽离出来做成一个单独的Context
  6. 拆分context对组件具有一定侵入性,需要到每个组件中修改context来源

我们的业务使用context的组件没有那么多,所以直接对context进行了拆分;但是对于dva,因为基本所有组件都会用到dva,所以不能直接拆用侵入性的方案,如下:

如何优化Dva、Redux

  • 场景
  1. 当我们在每个组件当中通过useSelector引用了global
  2. 可以通过globalData访问a、b数据,也可以访问其他在global下的其他字段,在开发过程中很方便
  3. 但是随之而来的就是跟context类似的问题,例如globalData有个c数据,A组件用到了a、b字段,B组件用到了c字段,当c字段被修改时,A组件也会更新
  • 解决方案一:

和context一样进行数据拆分,按需引入;但是这种方案有侵入性,且很不灵活,如果后续要用到其他字段,需要一直新增useSelector

  • 解决方案二:

这里先说明一下redux、dva的数据变化之后会发生更新的原理:

  1. useSelector内部调用了redux提供的subscribe方法订阅了数据变化
  2. useSelector内部监听到数据变化后,做了setState操作

针对以上逻辑,我们可以自己实现useSelector:

  1. 获取当前组件使用到了哪些字段,可以通过proxy拦截数据的方案来实现,
  2. 收到订阅通知后,不直接做setState操作,而是判断数据里面是否包含了对应的字段,如果包含才调用setState
  3. 具体代码实现

最后

知识无止境,更深更广的知识面能够更好地应付以后的突发需求,但有时候知识不一定要落地,额外的性能优化手段会增加代码复杂度需要一定的维护成本,有时候用React自带的性能优化api已经足够;要评估好业务的规模,不要为了一个永远都不会遇到的瓶颈而去浪费时间

相关推荐
杨进军11 分钟前
React 创建根节点 createRoot
前端·react.js·前端框架
ModyQyW26 分钟前
用 AI 驱动 wot-design-uni 开发小程序
前端·uni-app
说码解字32 分钟前
Kotlin lazy 委托的底层实现原理
前端
爱分享的程序员1 小时前
前端面试专栏-算法篇:18. 查找算法(二分查找、哈希查找)
前端·javascript·node.js
翻滚吧键盘1 小时前
vue 条件渲染(v-if v-else-if v-else v-show)
前端·javascript·vue.js
vim怎么退出1 小时前
万字长文带你了解微前端架构
前端·微服务·前端框架
你这个年龄怎么睡得着的1 小时前
为什么 JavaScript 中 'str' 不是对象,却能调用方法?
前端·javascript·面试
Java水解1 小时前
前端常用单位em/px/rem/vh/vm到底有什么区别?
前端
CAD老兵1 小时前
Vite 如何借助 esbuild 实现极速 Dev Server 体验,并支持无 source map 的源码调试
前端
南屿im1 小时前
JavaScript 手写实现防抖与节流:优化高频事件处理的利器
前端·javascript