破、审题
答题思路:
- 优化时机
- 定位方式
- 常见坑
-
优化时机
原理
React 会构建、维护一套内部的 VDOM 树,因为大量 DOM 时,操作 DOM 树相对操作 JS 对象更慢,依据 VDOM 树生成的差异更新真实 DOM。当每一个组件的 props 或 state 变更时,React 会对比新旧元素,决定是否有必要更新真实 DOM。当它们不同时,React 会更新该 DOM,此过程被称为:协调。
协调成本昂贵,若一次渲染的层级过深,就会阻塞 UI 主线程的执行,造成卡顿,引起页面帧率下降。时机
定位方式
还原场景、完整复现。
工具: Chrome Performance 面板
React Developer Tools 中的 Profiler 分析组件渲染次数、开始时间即耗时。
React Profiler 官网文档介绍常见坑
单个组件的更新,却触发了大量无关组件更新,这就有问题了。
解决方案:React.memo
和PureComponent
都是通过浅比较方式对比前后 props 和 state 的变化。
- 那在使用了如上两个方案后,是否会有失效的情况呢?
答:常见的坑就是使用 箭头函数。
<ListItem
key={id}
onXXX={(id) => {...}}
onXXXX={this.xxxx}
/>
以上代码,每次调用 render 函数时都会动态生成一个新的函数,函数的引用 变了,此时React.memo
无效。
这种 JSX 问题好解决,将整个函数提取为一个 类属性的函数 即可。
在
PureComponent
中,每次调用 render 函数都会生成一个新的 data 对象,PureComponent
破防了,若下层子组件没防护好,层层击穿,开始了昂贵的协调,如下代码:render() {
const data = this.props.list.map(item => item)
return (
<子组件
data={data}
renderItem={this.renderItem}
/>
)
}
处理方案
缓存
不可变数据
手动控制
shouldComponentUpdate
,render 阶段执行,此时形参(newProps, newState, newContext)
答题
如何避免重复渲染分为三个步骤:选择优化时机、定位重复渲染问题、引入解决方案。
优化时机:需根据业务标准和页面性能数据分析,来决定是否有必要。若卡顿业务可接受,必做,下一步,定位。
定位重复渲染问题:还原场景,复现问题,使用 Chrome Performance 面板、React Developer Tools 中的 Profiler 工具进行分析,对卡顿点、组件重复渲染次数及耗时排查性能问题。
解决方案:PureComponent
、React.memo
等组件缓存 API,减少重新渲染。若错误使用,会使其无效,如 JSX 的属性是 箭头函数,或每次生成新的对象,那就破防了。
针对这种破防,有三种解决方案:缓存
- 不可变数据
- 手动控制
通过以上手段就可避免无效渲染带来的性能问题了。