前言:我们从React源码入手,结合有道精品课大前端的具体业务,运用三大原则对系统进行外科手术式的优化。同时介绍React Profiler这款工具如何帮我们定位性能瓶颈前言:我们从React源码入手,结合有道精品课大前端的具体业务,运用三大原则对系统进行外科手术式的优化。同时介绍React Profiler这款工具如何帮我们定位性能瓶颈
作者/ 安增平
编辑/ Ein
React性能优化是在业务迭代过程中不得不考虑的问题,大部分情况是由于项目启动之初,没有充分考虑到项目的复杂度,定位该产品的用户体量及技术场景并不复杂,那么我们在业务前期可能并不需要考虑性能优化。但是随着业务场景的复杂化,性能优化就变得格外重要。
我们从React源码入手,结合有道精品课大前端的具体业务,运用优化技巧对系统进行外科手术式的优化。同时介绍一下React Profiler这款性能优化的利器是如何帮我们定位性能瓶颈的。
本文中的项目代码全部是在有道大前端组开发项目中的工作记录,如有不足欢迎在留言区讨论交流,笔芯❤
页面加载流程
- 假设用户首次打开页面(无缓存),这个时候页面是完全空白的;
- html 和引用的 css 加载完毕,浏览器进行首次渲染;
- react、react-dom、业务代码加载完毕,应用第一次渲染,或者说首次内容渲染;
- 应用的代码开始执行,拉取数据、进行动态import、响应事件等等,完毕后页面进入可交互状态;
- 接下来 lazyload 的图片等多媒体内容开始逐渐加载完毕;
- 直到页面的其它资源(如错误上报组件、打点上报组件等)加载完毕,整个页面加载完成。
我们主要来针对React进行剖析:
React 针对渲染性能优化的三个方向,也适用于其他软件开发领域,这三个方向分别是:
- 减少计算的量:React 中就是减少渲染的节点或通过索引减少渲染复杂度;
- 利用缓存:React 中就是避免重新渲染(利用 memo 方式来避免组件重新渲染);
- 精确重新计算的范围:React 中就是绑定组件和状态关系, 精确判断更新的’时机’和’范围’. 只重新渲染变更的组件(减少渲染范围)。
如何做到这三点呢?我们从React本身的特性入手分析。
React 工作流
React 是声明式 UI 库,负责将 State 转换为页面结构(虚拟 DOM 结构)后,再转换成真实 DOM 结构,交给浏览器渲染。State 发生改变时,React 会先进行Reconciliation,结束后立刻进入Commit阶段,Commit结束后,新 State 对应的页面才被展示出来。
React 的Reconciliation需要做两件事:
- 计算出目标 State 对应的虚拟 DOM 结构。
- 寻找「将虚拟 DOM 结构修改为目标虚拟 DOM 结构」的最优方案。
React 按照深度优先遍历虚拟 DOM 树的方式,在一个虚拟 DOM 上完成Render和Diff的计算后,再计算下一个虚拟 DOM。Diff 算法会记录虚拟 DOM 的更新方式(如:Update、Mount、Unmount),为Commit做准备。
React 的Commit也需要做两件事:
- 将Reconciliation结果应用到 DOM 中。
- 调用暴露的hooks如:componentDidUpdate、useLayoutEffect 等。
下面我们将针对三个优化方向进行精准分析。
减少计算的量
关于以上Reconciliation与Commit两个阶段的优化办法,我在实现的过程中遵循减少计算量的方法进行优化(列表项使用 key 属性)该过程是优化的重点,React 内部的 Fiber 结构和并发模式也是在减少该过程的耗时阻塞。对于Commit在执行hooks时,开发者应保证hooks中的代码尽量轻量,避免耗时阻塞,同时应避免在 CDM、CDU周期中更新组件。
列表项使用 key 属性