背景
看到一道react相关的面试题
1 | class App extends Component { |
最终渲染结果:**count 为5,count2为4**
可以先自己想一想。然后和我解析的对比一下。
原理
主要涉及到几个知识点。
- setState是异步操作函数
- js引擎事件处理机制
核心原理
count2 分析过程
在js执行的过程大致上是这样的:
1、componentDidMount过程
{count2: this.state.count2 + 1} 第一次进入setState函数内部,内部逻辑会将该对象放到一个数组里面。类似
1 | stateQueue = [{count2: 2}] |
将处理stateQueue的函数(我们称为flush())放入microtask队列
- 不知道的microtask队列的同学请访问深入理解单线程和事件循环机制
{count2: this.state.count2 + 1} 第二次进入setState函数内部,内部逻辑会将该对象继续放到数组里面。
1 | stateQueue = [ |
2、 macrotask队列
setTimeout(() => { this.setState({count2: this.state.count2 + 1}) }, 0) 进入macrotask队列俩次。
3、 此时js内存的状态
1 | macrotask队列 ---- [initMacrotask,setTimeout,setTimeout] |
4、单线程 事件循环机制
此时,initMacrotask执行完毕,开始执行microtask中的flush()
5、flush函数执行 (重点)
flush 内部会合并state,处理stateQueue队列。类似这样:
1 | let newStates = Object.assign(pesStates, [...stateQueue] ); |
合并之后,会调用render函数,进行diff算法处理。此时页面会显示 2
6、microtask队列清空
microtask队列执行完毕,会进行下一次的事件循环,调用macrotask队列里面的 setTimeout() 函数,此时又调用 this.setState() 函数,重复上述过程。此时js内存状态:
1 | stateQueue = [ |
7、第一个setTimeout执行完毕
此时又该调用microtask队列里面的 flush() 了。此时页面会显示 3
8、重复 6-7
最终 页面会显示 4
count 分析过程
根据上面的分析,count也是如此进行的。差别就在 stateQueue
1 | stateQueue = [ |
简化一下:
结尾
为了解决这道面试题,更加深入理解了 js引擎的事件循环机制,顺便啃了react相关的源码部分。
然后利用一周的零碎事件顺便写了一个简版的react。后面再慢慢总结一下react源码相关的学习。
To Be Continue!