Skip to content

Latest commit

 

History

History
139 lines (119 loc) · 4.22 KB

01.pure-render-checks.md

File metadata and controls

139 lines (119 loc) · 4.22 KB

保证渲染的性能

为了保障组件的性能, 我们有的时候会从组件渲染的角度出发.

更干净的render函数? 这个概念可能会有点让人疑惑.

其实在这里干净是指我们在shouldComponentUpdate这个生命周期函数里面去做浅比较, 从而避免不必要的渲染.

关于上面的干净渲染, 现有的一些实现包括React.PureComponent, PureRenderMixin, recompose/pure 等等.

第一个例子

坏实践
class Table extends PureComponent {
  render() {
    return (
      <div>
        {this.props.items.map(i =>
          <Cell data={i} options={this.props.options || []}/>
        )}
      </div>
    );
  }
}

这种写法的问题在于{this.props.options || []}- 这种写法会导致所有的Cell都被重新渲染即使只有一个cell发生了改变. 为什么会发生这种事呢?

仔细观察你会发现, options这个数组被传到了Cell这个组件上, 一般情况下, 这不会导致什么问题. 因为如果有其他的Cell组件, 组件会在有props发生改变的时候浅对比props并且跳过渲染(因为对于其他Cell组件, props并没有发生改变). 但是在这个例子里面, 当options为null时, 一个默认的空数组就会被当成Props传到组件里面去. 事实上每次传入的[]都相当于创建了新的Array实例. 在JavaScript里面, 不同的实例是有不同的实体的, 所以浅比较在这种情况下总是会返回false, 然后组件就会被重新渲染. 因为两个实体不是同一个实体. 这就完全破坏了React对于我们组件渲染的优化.

好实践
const defaultval = [];  // <---  也可以使用defaultProps
class Table extends PureComponent {
  render() {
    return (
      <div>
        {this.props.items.map(i =>
          <Cell data={i} options={this.props.options || defaultval}/>
        )}
      </div>
    );
  }
}

第二个例子

在render函数里面调用函数也可能导致和上面相同的问题.

坏实践
class App extends PureComponent {
  render() {
    return <MyInput
      onChange={e => this.props.update(e.target.value)}/>;
  }
}
又一个坏实践
class App extends PureComponent {
  update(e) {
    this.props.update(e.target.value);
  }

  render() {
    return <MyInput onChange={this.update.bind(this)}/>;
  }
}

在上面的两个坏实践中, 每次我们都会去创建一个新的函数实体. 和第一个例子类似, 新的函数实体会让我们的浅比较返回false, 导致组件被重新渲染. 所以我们需要在更早的时候去bind我们的函数.

好实践
class App extends PureComponent {
  constructor(props) {
    super(props);
    this.update = this.update.bind(this);
  }

  update(e) {
    this.props.update(e.target.value);
  }

  render() {
    return <MyInput onChange={this.update}/>;
  }
}
坏实践
class Component extends React.Component {
  state = {clicked: false};

  onClick() {
    this.setState({clicked: true})
  }

  render() {
    // 如果options为空的话, 每次都会创建一个新的{test:1}对象
    const options = this.props.options || {test: 1};

    return <Something
      options={options}
      // New function created each render
      onClick={this.onClick.bind(this)}
      // New function & closure created each render
      onTouchTap={(event) => this.onClick(event)
    />
  }
}
好实践
class Component extends React.Component {
  state = {clicked: false};
  options = {test: 1};

  onClick = () => {
    this.setState({clicked: true})
  };

  render() {
    // Options这个对象只被创建了一次.
    const options = this.props.options || this.options;

    return <Something
      options={options}
      onClick={this.onClick} // 函数只创建一次, 只绑定一次
      onTouchTap={this.onClick} // 函数只创建一次, 只绑定一次
    />
  }
}

参考资料: