React通过this.state来访问state,通过this.setState来更新state。当setState被调用的时候,React会重新调用render方法来重新渲染组件。
一.this.setState是异步的,异步的,异步的!!!
两种方法
1.我们时常来修改state里的值
this.setState({
num:10
})
2.我们想在修改完之后做一些事情
//后面接的是DOM渲染完毕的回调
this.setState({num:1},()=>{
console.log(this.state.num)//输出num=1
});
3.我们有时候需要在上一次的修改上在修改
this.setState((prevState, props)=>({
num :prevState.num+1
}))
二.setState循环调用风险
shouldComponentUpdate和componentWillUpdate中调用了setState,造成循环调用风险
三.什么是Batch Update ?
在React中,我们在componentDidMount生命周期连续调用SetState:
componentDidMount () {
this.setState({ foo: 1 })
this.setState({ foo: 2 })
this.setState({ foo: 3 })
}
没有Batch Update的情况下,上面的操作会导致三次组件渲染,但是使用Batch Update机制下时间上只运行了一次渲染。componentDidMount中三次对model的操作被优化为一次view更新,不必要的Vitual Dom计算被忽略,从而提高了框架的效率。
Batch Update的实现
我们想到的可能就是数据结构中的栈和队列,比较一下还是使用一个queue来保存update,并在合适的时机对这个queue进行flush操作。那么现在有两个问题:
什么时候创建这个queue
什么时候对这个queue进行flush
那么我们要对Reac和Vue的源码进行分析,首先React:React中的Batch Update是通过Transaction(事务)来实现的。在React源码关于Transaction的部分可以用一幅画解释:
Transaction对一个函数进行包装,让React有机会在一个函数执行前和执行后运行特定的逻辑,从而完成对整个Batch Update流程的控制。
简单的说就是在要执行的函数中用事务包裹起来,在函数执行前加入initialize阶段,函数执行,最后执行close阶段。那么Batch Update中
在事件initialize阶段,一个update queue被创建。在事件中调用setState方法时,状态不会被立即调用,而是被push进Update queue中。
函数执行结束调用事件的close阶段,Update queue会被flush,这事新的状态才会被应用到组件上并开始后续的Virtual DOM更新,biff算法来对model更新。
对比于React,Vue实现Batch update就简单多了:直接借助JS中的Event Loop。(参考阮老师的http://www.ruanyifeng.com/blog/2013/10/event_loop.html)
Vue中的核心代码就仅仅20多行,如下:
https://github.com/vuejs/vue/blob/dev/src/core/observer/scheduler.js#L122-L148
https://blog.csdn.net/qq_37210523/article/details/80922762
参考:https://zhuanlan.zhihu.com/p/28532725
总结
1.调用setState,组件的state并不会立即改变,setState只是把要修改的状态放入一个队列中,React会优化真正的执行时机,并且React会出于性能原因,可能会将多次setState的状态修改合并成一次状态修改。所以不要依赖当前的State,计算下个State。
2.react使用批量处理的方式,用事务函数进行包装,依赖语言特性的通用模式,因此有更稳定可控的表现
3.对比于React,Vue实现Batch update就简单多了:直接借助JS中的Event Loop