程序员技术栈程序员

react之setState运行机制

2018-04-04  本文已影响44人  神秘者007

React setState 运行机制

  • setState是同步操作,采用批处理机制
  • react采用了事务transaction设计思想
  • vue采用的浏览器的event-loop机制: 主队列,异步队列(微队列、宏队
    列),因此说vue是依赖了JS语言的特性,而不像大厂出品的react

先看一段代码,并判断输出值

  constructor(props){
    super(props);
    this.state = {
      count: 0
    };
  }
  componentDidMount(){
    let me = this;
    me.setState({
      count: me.state.count + 1
    });
    console.log('第1次:', me.state.count);
    me.setState({
      count: me.state.count + 1
    });//1
    console.log('第2次:', me.state.count);
    setTimeout(function(){
      me.setState({
        count: me.state.count + 1
      });
      console.log('第3次:', me.state.count);
    }, 0);
    setTimeout(function(){
      me.setState({
        count: me.state.count + 1
      });
      console.log('第4次:', me.state.count);
    }, 0);
  }
  render() {
    return (
      <div className="App">
        <p>{this.state.count}</p>
      </div>
    );
  }
}
export default App;
//页面显示 3,输出结果如下:
第1次:0
第2次:0
第3次:2
第4次:3

为什么不是 1 2 3 4呢? react采用批处理,因此react只会执行最后一个setState,前面的setState被react忽略掉;由于react无法将setTimeout加入到批处理当中,因此后面两个setTimeout里面setState单独执行,进而批处理剩下第二个setState,故而第1次:0
为什么不是 0 1 2 3呢? react采用事务的机制来处理setState,要先收集setState要做的事情,然后再之后的某一个时间点进行统一处理,注意:setState是同步的操作,是在主线程运行的,但它被抽出来了,执行事务的机制,先收集,然后往下走,相对来说,给我们的感觉像是异步的,因此第2次:0
接下来就是 第3次:2 第4次:3,setTimeOut不参与批处理和事务机制,因此就直出了

V15 版本的问题

由于react事务统一处理机制(统一收集,主线程统一执行),如果setState
的内容过大时,render时会造成页面的卡死,这一点在16版本进行了优化

  • vue是异步线程的操作,因此不会卡死

V16 版本的优化

为了解决15版本卡死的问题,V16提出了Fiber,已经更新的react-dom React Fiber

  • Reconciler即Virtul DOM, 用于计算新老View的差异(dom-diff)
  • React 16之前的 Reconciler 叫 Stack Reconciler
  • Fiber是React新的Reconciler,Fiber是一次重量级的核心架构的更换

Reconciler和render两个概念引出了phase概念

  • phase指的是React组件渲染时的阶段
    • Reconciler阶段: 依序遍历组件,通过diff算法,判断组件是否需要更新,给需要更新的组件加上tag,遍历在完成后,将所有带有tag的组件加到一个数组中,这个阶段的任务可以被打断,先执行新的重要任务,待执行完毕后,再回来继续Reconciler
    • Commit阶段: 根据在Reconciler阶段生成的数组,遍历更新DOM,这个阶段要一次性执行完


      image.png

Filber Reconciler VS Stack Reconciler

  • Stack: 以前react渲染相关的事务是连续的,一旦开始,就会在主线程渲染,其他任务必须等待,导致了主线程被阻塞,影响性能。
  • Fiber: 现在react事务是由一系列Fiber的更新组成,因此可以在多个帧中断断续续的更细Fiber,最后commit变化(将原来的整个Virtul DOM的更新任务拆分成一个个的小任务,每次做完一个小任务后,放弃一下自己的执行将主线程空闲出来,看看有没有其他任务,如果有,就暂停本次任务,执行其他任务,如果没有,就继续下一个任务)
  • 总结: V16把以前的一个大事务改为拆分成一个个小事务,标记为tag,优先级高的就打断事务的执行,释放主线程
上一篇下一篇

猜你喜欢

热点阅读