Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

记一次react渲染机制和不可变数据的理解 #7

Open
zp1112 opened this issue May 7, 2018 · 0 comments
Open

记一次react渲染机制和不可变数据的理解 #7

zp1112 opened this issue May 7, 2018 · 0 comments

Comments

@zp1112
Copy link
Owner

zp1112 commented May 7, 2018

React渲染

花了一天时间,看了很多文章,大体上是理解了react的render机制,以及不可变数据在render机制中起到的作用。

工具

安装一个工具可以检测组件是否发生了不必要的渲染。如果发生了不必要的组件渲染,控制台会精确定位并且打印出那些状态触发了不必要的渲染。然后针对性的进行优化,简直是神器啊。

npm i -D why-did-you-update

在index.js加

if (process.env.NOED_ENV !== 'production') {
  const { whyDidYouUpdate } = require('why-did-you-update');
  whyDidYouUpdate(React);
}

正常渲染

class App extends component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      user: {
        name: 'zp'
      }
    }
  }
  handleClick = () => {
    this.setState({
      count: 0
    });
  }
  render() {
    return (
      <div>
        <div onClick={this.handleClick}>点击我</div>
        <div>{this.state.count}</div>
      </div>
    );
  }
}

保存文件,打开页面,点击点击我,会发现控制台打印出了一个console,提示这个render是不必要的render,count并没有改变,但是组件却重新渲染了。

image

使用PureComponent

PureComponent是react自带的继承了component的类,但是在组件re-render的时候加了一层判断,浅比较新的状态和旧的状态是否一致,如果新旧状态的引用不一致,则比较第一层状态值是否改变,上面的例子中count为第一层,值没有改变,所以不重新渲染。

class App extends PureComponent {
// ...代码
}

为什么说是浅比较,那是因为假设我现在改更新的是user里面的name属性,name我们看看即便加了purecomponent,是否还会发生不必要的渲染呢。

修改handleClick

handleClick = () => {
    this.setState({
      user: {
        name: 'zp'
      }
    });
  }

此时页面console,依然打印出不必要渲染,告知这里的不必要渲染造成的原因是user的引用改变了。

由于purecomponent只比较第一层,所以第二层的user下面的值{name: 'zp'}是个引用。引用改变了,值就不一样了,就会触发re-render。相当于

image

deepEquals = (obj1, obj2) => { 
    if (obj1 === obj2) {
      return true;
    }
    for (const key of Object.keys(obj2)) {
      if (obj1.hasOwnProperty(key) && obj1[key] === obj2[key]) {
        return true;
      }
    }
    return false;
  }
  shouldComponentUpdate = (nextProps, nextState) => {
    return  !this.deepEquals(this.state, nextState) || !this.deepEquals(this.props, nextProps);
  }

手写shouldComponentUpdate和加purecomponent的效果是一样的,只能浅比较。

所以purecomponent解决了浅层次的dom渲染优化,但是对于结构更加复杂的嵌套的数据结构,还是会发生不必要的渲染。

immutable.js

网上很火的数据结构immutable.js,似乎很好的解决了这个问题,让我们看看如何简单的使用immutable.js改造上面的例子

首先本地安装immutable.js。

npm i --save immutable

这里我们先把state改造一下,使用immutable的API改造handleClick,最后render里面的dom渲染再使用API改造一下

this.state = {
      data: Immutable.fromJS({
        count: 0,
        user: {
          name: 'zp'
        }
      })
    }

handleClick = () => {
    this.setState((d) => {
      return {
        data: d.data.updateIn(['user', 'name'], () => 'zp') // 这里改变了user里面的name,深层改变
      }
    })
  }

render() {
    const data = this.state.data;
    return (
      <div>
        <div onClick={this.handleClick}>点击我</div>
        <div>{data.get('count')}</div>
        <div>{data.getIn(['user', 'name'])}</div>
      </div>
    );
  }

保存刷新页面,点击,会发现并没有多余的render。cool!

用不用

我觉得这样看来,immutable.js真的很强大很诱人,看到没有浪费的render,这让有强迫症的开发人员感到很舒服,但是网上看到immutable.js的弊端也有些,

  1. ImmutableJS 库体积比较大,大概56k,开启 gzip 压缩后16k。
  2. 学习成本。

如果不用ImmutableJS ,那么就要尽量避免使用复杂的结构,最好扁平化数据结构,但是。。。。那是不可能的!!所以,你们看着办吧。

我只想说,虽然我现在的项目并没有使用react,甚至我也没用过ImmutableJS ,但是我对react的爱是不变的,我会保持学习,一直进步。。。。嘿,小哥,招前端吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant