React性能优化: 剑走偏锋(一)

832 阅读2分钟

简介

关于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;
  }

如果你也遇到这一类的性能问题, 希望这个方案对你有所帮助.

下一节React性能优化: 剑走偏锋(二)继续讨论react性能优化问题