Appearance
主要实现在
commitMutationEffects
commitMutationEffects
js
function commitRootImpl(root, renderPriorityLevel) {
//..
try {
commitMutationEffects(root, renderPriorityLevel)
}
}
function commitMutationEffects(root, renderPriorityLevel) {
while (nextEffect !== null) {
const flags = nextEffect.flags
if (flags & ContentReset) {
// 重置文本
commitResetTextContent(nextEffect);
}
if(flags & Ref) {
// ref 的更新。。。
}
const primaryFlags = flags & (Placement | Update | Deletion | Hydrating)
// 根据不同的 flags 进行不同操作
switch (primaryFlags) {
case Placement: {
// 插入 DOM
commitPlacement(nextEffect)
nextEffect.flags &= ~Placement
}
case PlacementAndUpdate: {
// 插入dom 和更新
commitPlacement(nextEffect);
nextEffect.flags &= ~Placement;
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
//... ssr相关
case Update: {
// 更新
const current = nextEffect.alternate
commitWork(current, nextEffect)
break;
}
case Deletion: {
// 删除
commitDeletion(root, nextEffect, renderPriorityLevel)
break;
}
}
nextEffect = nextEffect.nextEffect;
}
}
commitPlacement
用于插入 DOM 节点
js
// ReactFiberCommitWork.js
function commitPlacement() {
// 获取最近的 HostParent 节点,也就是浏览器 div等节点,存在跨组件层级的情况
const parentFiber = getHostParentFiber(finishedWork);
const parentStateNode = parentFiber.stateNode;
switch (parentFiber.tag) {
case HostComponent:
parent = parentStateNode;
isContainer = false;
break;
case HostRoot:
parent = parentStateNode.containerInfo;
isContainer = true;
break;
case HostPortal:
parent = parentStateNode.containerInfo;
isContainer = true;
break;
//...
// 这里获取 insertBefore 的锚点,也就是在这个锚点之前插入
const before = getHostSibling(finishedWork);
// 插入操作
if (isContainer) {
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
} else {
insertOrAppendPlacementNode(finishedWork, before, parent);
}
}
插入操作过程中,如果插入的是组件节点,会递归寻找其子节点及其子节点的兄弟节点(HostComponent类型的),再进行插入
commitWork
当
Fiber节点
含有Update flags
,意味着该Fiber节点
需要更新。调用的方法为commitWork
,他会根据Fiber.tag
分别处理
主要关注FunctionComponent
和HostComponent
FunctionComponent mutation
调用
commitHookEffectListUnmount
该方法会遍历effectList
,执行所有useLayoutEffect hook
的销毁函数。
js
// ReactFiberCommitWork.js
function commitHookEffectListUnmount(flags, finishedWork) {
// 控制传入的 flags 执行不同hook
let updateQueue = finishedWork.updateQueue // fiber 更新队列对象
// lastEffect 是 fiber.updateQueue 内部属性对象
let lastEffect = updateQueue !== null ? updateQueue.lastEffect : null
if (lastEffect !== null) {
let firstEffect = lastEffect.next
let effect = firstEffect
do {
const destroy = effect.destroy
// 也就是 useLayoutEffect 返回的销毁函数
efect.destroy = undefined
if (destroy !== undefined) {
destroy()
}
effect = effect.next
} while (effect !== firstEffect)
}
}
HostComponent mutation
当
fiber.tag
为HostComponent
(也就是 div 等平台标签),会调用commitUpdate
。
js
// ReactDOMHostConfig.js
function commitUpdate(domElement, updatePayload, type, oldProps, newProps) {
updateProperties(domElement, updatePayload, type, oldProps, newProps)
}
// ReactDOMComponent.js
function updateProperties() {
//...
updateDOMProperties(
domElement,
updatePayload,
wasCustomComponentTag,
isCustomComponentTag,
);
}
function updateDOMProperties(domElm, updatePayload) {
// updatePayload 也就是之前update阶段得到的 updatePayload 数组
for (let i = 0; i < updatePayload.length; i += 2) {
const propKey = updatePayload[i];
const propValue = updatePayload[i + 1];
if (propKey === STYLE) {
// style 属性
setValueForStyles(domElement, propValue);
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
// innerHTML
setInnerHTML(domElement, propValue);
} else if (propKey === CHILDREN) {
// children
setTextContent(domElement, propValue);
} else {
// 其他属性
setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
}
}}
commitDeletion
js
function commitDeletion(finishedRoot, current) {
// 会递归删除子节点
unmountHostComponents(finishedRoot, current, renderPriorityLevel)
}
function unmountHostComponents(finishedRoot, current) {
// ...
while (true) {
let parent = current.return
findParent: while(true){
// 找到 最近的平台标签类型的 父节点(div...)
const parentStateNode = parent.stateNode
switch(parent.tag) {
case HostComponent: {
currentParent = parentStateNode;
break findParent ; // 跳出循环
}
case HostRoot
case HostPortal
//...
}
parent = parent.return
}
}
if (node.tag === HostComponent || node.tag === HostText) {
commitNestedUnmounts(finishedRoot, node, nearestMountedAncestor);
//...
removeChild(currentParent, current.stateNode) // 移除节点
}
}
删除过程
- 递归调用Fiber节点及其子孙Fiber节点中
fiber.tag
为classComponent
的componentWillUnmount
生命周期钩子(在递归调用过程中),从页面移除Fiber节点对应DOM节点 - 解绑ref
- 调度
useEffect
的销毁函数