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已经足够;要评估好业务的规模,不要为了一个永远都不会遇到的瓶颈而去浪费时间

相关推荐
无咎.lsy1 分钟前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec8 分钟前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec11 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆1 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
twins35202 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky2 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~2 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n03 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。3 小时前
案例-任务清单
前端·javascript·css