简介
关于react的性能优化, 可参考的资料很多, 比如:
- 合理的拆分组件(容器组件, 渲染组件)
- 合理的数据设计, 只传递组件依赖的数据.
- 使用PureComponent
- 使用Memo.
- 使用Immutable数据结构, 提升数据的比较效率
- 重写shouldComponentUpdate生命周期.
等等手段, 都有助于提升项目的性能. 这些手段中, 最容易想到, 也最容易实现的, 就是重新shouldComponentUpdate方法.
场景介绍
一个页面的容器组件, 依赖几十个数据对象, 这些数据可能是简单的对象, 也可能是复杂对象或immutable对象. 其中store上任何一个数据的更新, 都会引起组件的重新渲染, 而组件的重新渲染, 又可能重新更新store上的数据. 而store上的数据更新, 又会引起其他组件的重新渲染. 如此循环, 可能导致的问题是, 一个简单的数据更新, 会引起容器组件以及子组件反复的渲染. 多达10几20次的中间临时渲染. 这些临时的渲染其实都是不必要的.
解决方案: 优化手段
重写shouldComponentUpdate生命周期. 永远都返回false, 最后一次, 手动的调用组件的forceUpdate方法, 更新组件渲染。
shouldComponentUpdate() {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.forceUpdate();
}, 0);
return false;
}
该方法, 看起来看起来很粗暴, 但是效果明显. 而且简单. 目前已经运用于项目中. 但该方法有一个不足之处是如果是一个连续的操作, 比如鼠标拖拽, 移动. 操作会有些跳动, 体验不是很好. 解决的方法是: 针对这类操作, 我们返回true, 把更新控制权交个react本身. 毕竟这一类的操作占项目的比例有限, 对我们的性能影响有限.
更改后的代码如下:
shouldComponentUpdate() {
// 在做连续操作时, 调用froceUpdate方法会跳动, 导致交互体验不友好.
// 针对连续的操作, 我们在全局设置一个标识, 以开启默认的react render.
// 连续的操作: 鼠标拖拽添加frame, 元素旋转, 元素resize, 移动.
const globalController = __app['isAutoRender'];
const isAutoRender = globalController && globalController.isAutoRender;
if (isAutoRender) {
return true;
}
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.forceUpdate();
}, 0);
return false;
}