react源码学习--背景

背景

看到一道react相关的面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class App extends Component {
state = {
count: 1,
count2: 1
}
componentDidMount() {
// 5
this.setState({count: ++this.state.count})
this.setState({count: ++this.state.count})
setTimeout(() => { this.setState({count: ++this.state.count}) }, 0)
setTimeout(() => { this.setState({count: ++this.state.count}) }, 0)

// 4
this.setState({count2: this.state.count2 + 1})
this.setState({count2: this.state.count2 + 1})
setTimeout(() => { this.setState({count2: this.state.count2 + 1}) }, 0)
setTimeout(() => { this.setState({count2: this.state.count2 + 1}) }, 0)
}
render() {
return (
<div className="App">
<p>{this.state.count}</p>
{this.state.count2}
</div>
);
}
}

最终渲染结果:count 为5,count2为4
可以先自己想一想。然后和我解析的对比一下。

原理

主要涉及到几个知识点。

  • setState是异步操作函数
  • js引擎事件处理机制

核心原理

count2 分析过程

在js执行的过程大致上是这样的:

1、componentDidMount过程
{count2: this.state.count2 + 1} 第一次进入setState函数内部,内部逻辑会将该对象放到一个数组里面。类似

1
stateQueue = [{count2: 2}]

将处理stateQueue的函数(我们称为flush())放入microtask队列

{count2: this.state.count2 + 1} 第二次进入setState函数内部,内部逻辑会将该对象继续放到数组里面。

1
2
3
4
stateQueue = [
{count2: this.state.count + 1},
{count2: this.state.count + 1},
]

2、 macrotask队列
setTimeout(() => { this.setState({count2: this.state.count2 + 1}) }, 0) 进入macrotask队列俩次。
3、 此时js内存的状态

1
2
macrotask队列 ---- [initMacrotask,setTimeout,setTimeout]     
microtask队列 ---- [flush]

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
2
3
4
5
stateQueue = [
{count2: 2},
]
macrotask队列 ---- [setTimeout,setTimeout]
microtask队列 ---- [flush]

7、第一个setTimeout执行完毕
此时又该调用microtask队列里面的 flush() 了。此时页面会显示 3
8、重复 6-7
最终 页面会显示 4

count 分析过程

根据上面的分析,count也是如此进行的。差别就在 stateQueue

1
2
3
4
5
stateQueue = [
{count: ++this.state.count},
{count: ++this.state.count},
]
let newStates = Object.assign(pesStates, [...stateQueue] );

简化一下:

结尾

为了解决这道面试题,更加深入理解了 js引擎的事件循环机制,顺便啃了react相关的源码部分。
然后利用一周的零碎事件顺便写了一个简版的react。后面再慢慢总结一下react源码相关的学习。
To Be Continue!