blog icon indicating copy to clipboard operation
blog copied to clipboard

React优化:竭尽全力的减少render渲染

Open vibing opened this issue 6 years ago • 0 comments

前言

玩过React的同学都知道,render()方法除了第一次组件被实例化,其他情况绝大多数是state改变触发的。

而render方法的执行,所带来的负担就是重新对比Virtual DOM(虚拟DOM树),也就是重新执行Diff算法,然后把要修改的的DOM重新update。

而我们总是在开发过程中会产生非常多不必要的重新渲染

如何减少render的触发,是提升项目性能的关键之一。

入手点

减少render的入口在哪?当然是要知道哪些情况会触发render啦~

根据个人了解,触发render有以下两种情况:

  1. 组件实例化
  2. state变化、props变化

那么我们从这两点入手

优化

Immutable 和 componentShouldUpdate

《React和Immutable》中,提到了使用Immutable配合React的componentShouldUpdate生命周期函数来减少不必要的render,效果非常明显,这里就不说了,建议大家一定去看一下

除了使用 Immutable ,还有其他方法来避免render执行吗?

先看看正常开发中,使用setState的情况:

constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    console.log('render');
    return (
      <div>
        <input onChange={this.change} />
        <button onClick={this.submit}>提交</button>
      </div>
    );
  }

 change = e => {
    this.setState({
      value: e.target.value
    });
  };

  submit = () => {
    console.log(this.state.value);
  };

执行上面代码,然后在input里输入123,看看控制台上发生了什么:

4

对没错,执行了3次render,我们想象一下,用户在登录界面输入账号和密码时,render 会执行多少次啊啊啊 简直要命。

那有什么方法可以解决呢?

使用防抖来减少render

防抖函数应用在高频率操作时,能避免无意义的代码执行,从而提高性能。

constructor(props) {
    super(props);
    this.state = {
      inputVal: ''
    };
  }

  render() {
    console.log('render');
    return (
      <div style={{ margin: 50 }}>
        <input onChange={this.change} />
        <button onClick={this.submit}>提交</button>
      </div>
    );
  }

  timer = null;
  change = e => {
    //获取inputVal
    const inputVal = e.target.value;

    //防抖处理
    clearTimeout(this.timer);
    this.timer = setTimeout(() => {
      //set值
      this.setState({
        inputVal
      });
    }, 200);
  };

  submit = () => {
    console.log(this.state.inputVal);
  };

我们依然将inputVal挂在state上面,由于onChange事件可以频繁的触发setState,所以我们从这里下手,在onChange事件里使用防抖来避免频繁触发setState,从而避免多次render带来的性能消耗。

我们在文本框里输入值,然后看看控制台打印的结果:

7

Yes! 可以看到 我们输入 Hello,控制台只打印出了一次 render ,非常好!

在开发中可以将防抖函数放在工具函数中方便调用哟~

减少setState 使用私有属性

现在我们改变一种写法:不使用setState而是换成私有属性this.inputVal来代替:

constructor(props) {
    super(props);
    this.inputVal;
  }

  render() {
    console.log('render');
    return (
      <div style={{ margin: 50 }}>
        <input onChange={this.change} />
        <button onClick={this.submit}>提交</button>
      </div>
    );
  }

  change = e => {
    this.inputVal = e.target.value;
  };

  submit = () => {
    console.log(this.inputVal);
  };

我们仍然输入123,然后看看控制台:

5

哇塞~~ render一次都没执行,现在点击提交,打印结果是 123,这正是我们想要的。

缺点

聪明的同学能发现一个问题,其实这样的优化有一个缺点:不能使用双向绑定

在开发中,真正使用双向绑定的表单元素,一般会有其他效果必须依赖该表单元素的数据,比如省市区级联,市区的变化必须依赖省的数据。

所以如果没有必要使用双向绑定的,请尽量避免使用。

总结

减少render的执行次数,可以使用:

  • Immutable 配合 shouldComponentUpdate 具体请看这里
  • 使用防抖函数来减少不必要的setState,从而减少render次数
  • 使用私有属性来替代setState

vibing avatar May 25 '18 13:05 vibing