为什么需要合成事件
- 兼容浏览器监听写法
- 避免大量节点绑定事件占用内存,将事件委托到
document上,有统一的事件处理函数,等事件冒泡到document上时,react从target节点往上遍历父元素,判断有没有元素绑定对应事件,有则触发该事件。 - 事件回调函数处理完成后需要清空state队列中的更新,重新渲染视图。
事件持久化
event.persist,每当事件回调函数被触发后,合成事件对象都会被清空,保证其垃圾回收。如果需要异步处理事件,则需要执行event.persist,需要的事件对象会被保留,清空操作的对象则是一个新创建的对象。SyntheticEvent就是合成事件对象,这个对象会被重复使用,避免对象的重复创建开销。
setState
当执行setState时,先将当前要更新的state存储在Updater组件实例上,然后判断当前是否处于批量更新状态,如果是,则将当前Updater实例存储下来,等到非批量更新的时候一起更新。
在处理事件的时候,因为可能在回调函数中的动作会引起多个组件的更新,为了性能优化,react会进入批量更新状态。在代码中就是在统一的事件处理函数中将状态设置为批量更新状态。等当前事件执行结束后(遍历完所有的父节点,执行完所有的回调函数)将状态设置为false。并且执行batchUpdate批量执行更新逻辑。

state={count: 0}
handleClick = () => {
this.setState({
count: this.state.count + 1
})
// 0
console.log(this.state.count)
this.setState((prevState) => ({
count: prevState.count + 1
}))
// 0
console.log(this.state.count)
setTimeout(() => {
this.setState((prevState) => ({
count: prevState.count + 1
}))
// 3
console.log(this.state.count)
this.setState((prevState) => ({
count: prevState.count + 1
}))
// 4
console.log(this.state.count)
}, 0)
}
执行更新逻辑时,如果render函数返回的是函数组件,前后对比两次函数执行结果,进行dom-diff,执行需要的节点更新逻辑。如果返回的都是类组件,并不是直接调用render方法,将前后结果进行比对,而是调用返回组件实例上的updater. emitUpdate方法,这样的话,就会执行子组件中的componentShouldUpdate方法判断此次是否需要更新,如果需要则执行forceUpdate方法再次执行dom-diff逻辑判断,这个时候子组件返回的就是普通的原生节点,按原有逻辑进行比对即可。
在构造函数被new之后,componentWillMount被调用,调用实例的render函数,会递归解析返回的元素,解析子组件,实例化子组件,子组件componentWillMount被调用,调用子组件render函数,componentDidMount完成所有的子元素解析后,执行父组件的componentDidMount后完毕。当父组件调用setState,生命周期执行顺序为
- 父组件
shouldComponentUpdate - 父组件
componentWillUpdate - 子组件
componentWillReceiveProps - 子组件
shouldComponentUpdate - 子组件
componentWillUpdate - 子组件
componentDidUpdate - 父组件
componentDidUpdate
为什么不能将index作为key
因为react在dom-diff对比时,会将老节点列表中的key相同的元素保留作为新节点列表中对应key的元素。
dom-diff规则
- 对树来说,如果出现组件移动的情况,不处理,直接销毁组件重新生成
- 如果组件/节点类型发生变化则直接替换
- 同一层级的dom变更:增加、删除、移动(删除+插入 )
dom-diff算法
- 将老节点信息存储在一个对象中,键名为
key,键值为虚拟dom对象 - 遍历新子元素数组,先去对象中查找有没有相同
key的虚拟dom,如果有,则复用该虚拟dom并更新节点属性,再判断一下虚拟dom在原节点层次中的位置,如果比lastIndex小,则移动(删除当前节点,将虚拟dom拷贝到新位置),否则就不动(lastIndex会在遍历时变大)。相当于节点会不断往右移动。lastIndex用于判断可复用的节点是否需要移动。