1.作用
1.根据是否中断和错误的类型采用不同的处理方法 在renderRoot中我们catch到workloop的错误,我们只是给节点进行相应的标记,然后继续循环 具体的处理还是在completeunitofwork中 completeunitofwork是在每一个调和到叶节点执行的函数
2.判断是否有兄弟节点 判断自己有兄弟则返回自己的兄弟 否则继续往上看节点是否有兄弟节点 返回最近的兄弟,继续performunitofwork
3.完成节点之后 赋值effect链 方便后面的commit阶段 挂载真正的dom 是在performunitofwork上标记这个节点需要的操作在fiber的effect上,completeunitofwork就是把effect进行串联 方便commit根据这个链进行每个节点最终的操作
effect list 中的effect就是个fiber节点 告诉我们哪个节点需要更新的
effect的数据结构
{
type:"",//!需要更新的元素的type
updateQueue:[]//!哪个属性要更新成哪个值
}
复制代码
所以react里面的virtual dom以最小化的形式去更新dom以提升性能· 这个链就是在completeunitofwork形成
2.sideeffect
export type SideEffectTag = number;
// Don't change these two values. They're used by React Dev Tools.
export const NoEffect = /* */ 0b00000000000;//!不更新
export const PerformedWork = /* */ 0b00000000001;//!dev-tool用
// You can change the rest (and add more).
export const Placement = /* */ 0b00000000010;//挂载 commit阶段需要挂载的元素 会被放到effect链上
export const Update = /* */ 0b00000000100;//!更新 commit阶段需要更新的元素 会被放到effect链上
export const PlacementAndUpdate = /* */ 0b00000000110;
export const Deletion = /* */ 0b00000001000;
export const ContentReset = /* */ 0b00000010000;
export const Callback = /* */ 0b00000100000;
export const DidCapture = /* */ 0b00001000000;
export const Ref = /* */ 0b00010000000;
export const Snapshot = /* */ 0b00100000000;
// Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = /* */ 0b00110100100;
// Union of all host effects
export const HostEffectMask = /* */ 0b00111111111;
export const Incomplete = /* */ 0b01000000000;
export const ShouldCapture = /* */ 0b10000000000;
复制代码
3.effect链形成过程
比如在Levelup组件上setstate 其中b,c的span里的children发生改变 那么执行叶组件的completeunitofwork,先是a的span 但是这个fiber没有effect list 且a本身也没有effect 因为他属性没发生变化是不会增加effect tag的,然后就到了b b发生啦变化 会有effect tag 但是他没有effect-list 因为他没有子元素 所以Levelup组件的effect-list就增加啦b的fiber对象 c同理 button没发生变化同理a 到LevelUp啦 Levelup执行completeunitofwork给App组件链上自己的effect-list即b和c 但是自身没有发生变化 因为类组件只有生命周期等变化才会有effect,所以App的effect-list就是b->c 依次推到root root的effect-list就是b->c
4.重置childExpirationTime
completeUnitofwork中重要的一步 这个属性是这个fiber的孩子fiber的优先级最高的更新的expirationTime 因为我们schedulework时候加入调度队列的一直是root节点,某i一个节点下面有多个子树 每个子树的节点的任务的expirationTime都不一样 通过childExpirationTime来在root上面快速的找到子树优先级最高的任务
图片如上
比如Levelup这个节点 他的childExpirationTime 就是他的孩子节点里面优先级最高任务的childExpirationTime,i比如a,b同时有个异步任务
但是a优先级高 此时Levelup的childExpirationTime 就是a的那个任务 同时节点fiber的expirationTime也要考虑进来 选出最小的
5.completeWork
1.pop出context 因为之前performunitwork时候push进去啦context
2.对应原生标签会有初始化和更新
3.初始化监听事件
6.completework中updateHostComponent
1.diff properties 计算需要更新的内容 即**对比virtual dom来判断节点是否需要更新 **以此来最低更新dom
2.不同dom property有不同的处理
7.renderRoot中的catch错误处理
1.给报错节点增加incomplete副作用
2.给父链上具有error boundary的节点增加副作用
3.创建错误相关的更新
就是这个错误会一直往上直到找到可以处理错误的类组件 如果没有就找到root组件,getDerivedStateFromError/componentDidCatch 有这两个生命周期函数就是可以处理错误组件, 然后让这个update入队列 以及在报错的fiber加上Incomplete这个effecttag,在处理错误的类组件fiber上加上
ShouldCapture这个effecttag,然后交给unwindwork处理
8.unwindwork以及错误的捕获
1.对不同组件有不同处理
2.对shouldcapture组件加上didcapture副作用
流程
renderRoot中捕获到的错误会进行throwexception 函数 这个函数会往上找可以处理异常的类组件(有getDerivedStateFromError/componentDidCatch两个生命周期中的一个) 然后给发生异常的组件fiber一个incomplete的effecttag,捕获异常组件一个shouldcapture的effecttag 且创建一个update payload是getDerivedStateFromError返回的对象(对state的更新),callback是执行componentDidCatch生命周期且打印报错栈。
执行完throwexception 函数会直接进入completeunitofwork 这个时候该组件incomplete,会执行unwindwork,这个函数对可以处理异常的类组件进行处理,把shouldcapture这个effecttag制空然后加上didcapture,最重要是这个函数返回值,当当前组件时可以捕获异常的类组件时候返回这个类组件fiber,不是的时候返回Null,completeunitofwork 对这个返回值next继续处理,当这个值是null的时候,我们直接找父元素,将其父元素的effecttag置为incomplete 则当这个组件fiber执行完completeunitofwork ,其父元素继续执行completeunitofwork ,因为此时没有return 所以继续while(true)流程,处理方法同上,当我们这个fiber是可以捕获异常的组件时候,我们next就是这个fiber,这个时候返回这个next,这时候重新执行performunitofwork,重新更新我们这个节点,不过我们在处理updateQueue时候有一个update是error的update,我们会更新state为这个payload,然后重新renderchildren,和执行该upload的callback.(commit阶段执行)
即当我们levelup 抛出一个异常 我们App如果可以捕获异常 那么他的fiber将加上一个upload,然后levelup进入completework,发现他不可以捕获异常就继续往上找APP执行completework,他是可以捕获异常的就会返回这个fiber,执行performwork继续更新,执行upload,更新state,继续渲染子元素。