Skip to content
On this page

主要实现在 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);
		}
		ifflags & 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分别处理

主要关注FunctionComponentHostComponent

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.tagHostComponent(也就是 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.tagclassComponentcomponentWillUnmount 生命周期钩子(在递归调用过程中),从页面移除Fiber节点对应DOM节点
  • 解绑ref
  • 调度 useEffect 的销毁函数