书籍传送门:React技术揭秘——卡颂
前言:整篇文章,我都将以
问题
+笔记
的形式作为精读的导语展开叙述~文末有彩蛋喔~~~
上一篇文章中,我们已经了解了架构篇的Render
阶段,这一节我们继续上一节,聊一下Commit
阶段。
Commit
阶段主要分为before mutation
、mutation
、layout
三个阶段。
commitRoot(root);
上一章聊到commitRoot
是commit
工作的起点
下面就正式介绍 commit
阶段的流程:
before mutation
- DOM
操作前
mutation
- DOM
操作中
layout
- DOM
操作完毕
在介绍before mutation
之前,先回忆一件事:
V16
之后,给部分componentWillxxx
打上了UNSAFE_
标记,这个 xxx分别指的是:componentWillMount
,componentWillRecieveProps
、componentWillUpdate
.
为什么要加UNSAFE_
呢?
- 因为在
concurrent
模式下,render
阶段会被多次打断重新开始,所以就会导致这些在render
阶段执行的生命周期钩子,重复执行,导致数据不准或者死循环。
其实,大家也有注意到,出现的新的getDerivedStateFromProps
就是替换componentWillRecieveProps
的,而getSnapshotBeforeUpdate
则是替换componentWillUpdate
.
至于更加详细的差别,可以参考卡老师的:深入源码剖析componentWillXXX为什么UNSAFE : https://juejin.cn/post/6847902224287285255#comment
好了,现在开始进入这个阶段在做的三个步骤介绍:
处理
DOM
节点渲染/删除后的autoFocus
、blur
逻辑。调用
getSnapshotBeforeUpdate
生命周期钩子。调度
useEffect
。
我们着重关注下 useEffect
,这里 useEffect
是异步的。
整个 useEffect
异步调用分为三步:
before mutation
阶段在scheduleCallback
中调度flushPassiveEffects
layout
阶段之后将effectList
赋值给rootWithPendingPassiveEffects
scheduleCallback
触发flushPassiveEffects
,flushPassiveEffects
内部遍历rootWithPendingPassiveEffects
为什么 useEffect
是异步的呢?
与
componentDidMount
、componentDidUpdate
不同的是,在浏览器完成布局与绘制之后,传给useEffect
的函数会延迟调用。这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因此不应在函数中执行阻塞浏览器更新屏幕的操作。
useEffect
异步执行的原因主要是防止同步执行时阻塞浏览器渲染。
!!!预留两个疑问,下篇文章中统一解答,同样也是我自己的疑问:
一个组件中多个
useEffect
的执行顺序是固定的吗?一个组件中如果出现多个
useEffect
和useLayoutEffect
,执行顺序是怎样的?嵌套组件中的
useEffect
和useLayoutEffect
的执行顺序又是怎样的?
接下来,我们开始介绍 mutation
阶段:
类似 before mutation
阶段,mutation
阶段也是遍历 effectList
,执行函数。这里执行的是 commitMutationEffects
。
- 根据
ContentReset effectTag
重置文字节点 - 更新
ref
- 根据
effectTag
分别处理,其中effectTag
包括(Placement | Update | Deletion | Hydrating)
Placement effect
Fiber
节点对应的 DOM
节点需要插入到页面中
- 获取父级
DOM
节点。其中finishedWork
为传入的Fiber
节点。 - 获取
Fiber
节点的DOM
兄弟节点 - 根据
DOM
兄弟节点是否存在决定调用parentNode.insertBefore
或parentNode.appendChild
执行DOM
插入操作。
值得注意的一点是:Fiber
结构并不会和 DOM
结构完全一致,所以递归 Fiber
去找 DOM
结构时,复杂度会提升,且是指数倍。
Update effect
区分
Host Component
和Function Component
Host Component
中主要做了几件事- 处理
style
- 处理
DANGEROUSLY_SET_INNER_HTML
- 处理
children
- 处理剩余
props
(通常指的是原生dom
节点的一些属性值 )
- 处理
Function Component
中主要做了一件事- 调用
useLayoutEffect
的销毁函数
- 调用
Deletion effect
- 递归调用
Fiber
节点及其子孙Fiber
节点中fiber.tag
为ClassComponent
的componentWillUnmount
(opens new window)
生命周期钩子,从页面移除Fiber
节点对应DOM
节点 - 解绑
ref
- 调度
useEffect
的销毁函数