08.commitWork
同学们好,这一小节我们就来实现render函数中commitWork部分的逻辑。
packages/react-reconciler/src/ReactFiberWorkLoop.js
diff
import { scheduleCallback } from "scheduler";
import { createWorkInProgress } from "./ReactFiber";
import { beginWork } from "./ReactFiberBeginWork";
import { completeWork } from "./ReactFiberCompleteWork";
+import { NoFlags, MutationMask } from "./ReactFiberFlags";
+import { commitMutationEffectsOnFiber } from './ReactFiberCommitWork';
let workInProgress = null;
/**
* 在 Fiber 上计划更新根节点。
* @param {*} root - 根节点。
*/
export function scheduleUpdateOnFiber(root) {
ensureRootIsScheduled(root);
}
/**
* 确保根节点被调度执行。
* @param {*} root - 根节点。
*/
function ensureRootIsScheduled(root) {
scheduleCallback(performConcurrentWorkOnRoot.bind(null, root));
}
/**
* 执行根节点上的并发工作。
* @param {*} root - 根节点。
*/
function performConcurrentWorkOnRoot(root) {
renderRootSync(root);
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
commitRoot(root);
}
/**
* 提交根节点。
* @param {*} root - 根节点。
*/
function commitRoot(root) {
+ const { finishedWork } = root;
+ const subtreeHasEffects = (finishedWork.subtreeFlags & MutationMask) !== NoFlags;
+ const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags;
+ if (subtreeHasEffects || rootHasEffect) {
+ commitMutationEffectsOnFiber(finishedWork, root);
+ }
+ root.current = finishedWork;
}
/**
* 准备一个新的工作栈。
* @param {*} root - 根节点。
*/
function prepareFreshStack(root) {
workInProgress = createWorkInProgress(root.current, null);
}
/**
* 同步渲染根节点。
* @param {*} root - 根节点。
*/
function renderRootSync(root) {
prepareFreshStack(root);
workLoopSync();
}
/**
* 同步工作循环。
*/
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
/**
* 执行一个工作单元。
* @param {*} unitOfWork - 工作单元。
*/
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
const next = beginWork(current, unitOfWork);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
}
/**
* 完成一个工作单元。
* @param {*} unitOfWork - 工作单元。
*/
function completeUnitOfWork(unitOfWork) {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
completeWork(current, completedWork);
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
}
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
}packages/react-reconciler/src/ReactFiberCommitWork.js
js
import { appendChild, insertBefore } from "react-dom-bindings/src/client/ReactDOMHostConfig";
import { Placement, MutationMask } from "./ReactFiberFlags";
import { HostComponent, HostRoot, HostText } from "./ReactWorkTags";
/**
* 递归遍历所有子节点并在每个fiber上应用mutation副作用
* @param {Fiber} root - Fiber树的根节点
* @param {Fiber} parentFiber - 当前fiber节点的父节点
*/
function recursivelyTraverseMutationEffects(root, parentFiber) {
if (parentFiber.subtreeFlags & MutationMask) {
let { child } = parentFiber;
while (child !== null) {
commitMutationEffectsOnFiber(child, root);
child = child.sibling;
}
}
}
/**
* 应用fiber节点上的调和副作用
* @param {Fiber} finishedWork - 已完成的工作单位,即fiber节点
*/
function commitReconciliationEffects(finishedWork) {
const { flags } = finishedWork;
if (flags & Placement) {
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement;
}
}
/**
* 判断是否为宿主父节点
* @param {Fiber} fiber - fiber节点
* @returns {Boolean} 是宿主父节点则返回true,否则返回false
*/
function isHostParent(fiber) {
return fiber.tag === HostComponent || fiber.tag == HostRoot;
}
/**
* 获取fiber节点的宿主父节点
* @param {Fiber} fiber - fiber节点
* @returns {Fiber} fiber节点的宿主父节点
*/
function getHostParentFiber(fiber) {
let parent = fiber.return;
while (parent !== null) {
if (isHostParent(parent)) {
return parent;
}
parent = parent.return;
}
}
/**
* 将节点插入或附加到父节点
* @param {Fiber} node - fiber节点
* @param {Node} before - 参考节点
* @param {Node} parent - 父节点
*/
function insertOrAppendPlacementNode(node, before, parent) {
const { tag } = node;
const isHost = tag === HostComponent || tag === HostText;
if (isHost) {
const { stateNode } = node;
if (before) {
insertBefore(parent, stateNode, before);
} else {
appendChild(parent, stateNode);
}
} else {
const { child } = node;
if (child !== null) {
insertOrAppendPlacementNode(child, before, parent)
let { sibling } = child;
while (sibling !== null) {
insertOrAppendPlacementNode(sibling, before, parent)
sibling = sibling.sibling;
}
}
}
}
/**
* 获取宿主兄弟节点
* @param {Fiber} fiber - fiber节点
* @returns {Node|null} 如果存在宿主兄弟节点则返回,否则返回null
*/
function getHostSibling(fiber) {
let node = fiber;
siblings: while (true) {
while (node.sibling === null) {
if (node.return === null || isHostParent(node.return)) {
return null;
}
node = node.return;
}
node = node.sibling;
while (node.tag !== HostComponent && node.tag !== HostText) {
if (node.flags & Placement) {
continue siblings;
} else {
node = node.child;
}
}
if (!(node.flags & Placement)) {
return node.stateNode;
}
}
}
/**
* 提交位置
* @param {Fiber} finishedWork - 已完成的工作单位,即fiber节点
*/
function commitPlacement(finishedWork) {
const parentFiber = getHostParentFiber(finishedWork);
switch (parentFiber.tag) {
case HostRoot: {
const parent = parentFiber.stateNode.containerInfo;
const before = getHostSibling(finishedWork);
insertOrAppendPlacementNode(finishedWork, before, parent);
break;
}
case HostComponent: {
const parent = parentFiber.stateNode;
const before = getHostSibling(finishedWork);
insertOrAppendPlacementNode(finishedWork, before, parent);
break;
}
}
}
/**
* 遍历fiber树并在每个fiber上应用mutation副作用
* @param {Fiber} finishedWork - 已完成的工作单位,即fiber节点
* @param {Fiber} root - fiber树的根节点
*/
export function commitMutationEffectsOnFiber(finishedWork, root) {
switch (finishedWork.tag) {
case HostRoot:
case HostComponent:
case HostText: {
recursivelyTraverseMutationEffects(root, finishedWork);
commitReconciliationEffects(finishedWork);
break;
}
}
}packages/react-dom-bindings/src/client/ReactDOMHostConfig.js
diff
// 引入初始属性设置函数
import { setInitialProperties } from './ReactDOMComponent';
/**
* 判断是否需要设置文本内容
*
* @param {string} type - DOM元素的类型
* @param {Object} props - 元素属性对象
* @return {boolean} - 如果children属性是字符串或数字,返回true,否则返回false
*
* shouldSetTextContent函数用于判断基于给定的属性,是否应该设置DOM元素的文本内容。
*/
export function shouldSetTextContent(type, props) {
return typeof props.children === "string" || typeof props.children === "number";
}
/**
* 创建文本节点实例
*
* @param {string} content - 文本内容
* @return {Text} - 创建的文本节点
*
* createTextInstance函数用于创建一个新的文本节点,其中的内容是传入的content参数。
*/
export function createTextInstance(content) {
return document.createTextNode(content);
}
/**
* 创建DOM元素实例
*
* @param {string} type - DOM元素的类型
* @return {HTMLElement} - 创建的DOM元素
*
* createInstance函数用于创建一个新的DOM元素,元素类型由传入的type参数指定。
*/
export function createInstance(type) {
const domElement = document.createElement(type);
return domElement;
}
/**
* 将初始子节点附加到父节点
*
* @param {HTMLElement} parent - 父节点
* @param {HTMLElement|Text} child - 子节点
*
* appendInitialChild函数用于将子节点附加到指定的父节点。
*/
export function appendInitialChild(parent, child) {
parent.appendChild(child);
}
/**
* 为DOM元素设置初始属性
*
* @param {HTMLElement} domElement - 目标DOM元素
* @param {string} type - DOM元素的类型
* @param {Object} props - 需要设置的属性对象
*
* finalizeInitialChildren函数用于在DOM元素创建完成后,设置其初始属性。
*/
export function finalizeInitialChildren(domElement, type, props) {
setInitialProperties(domElement, type, props);
}
+/**
+ * 将子节点附加到父节点
+ *
+ * @param {HTMLElement} parentInstance - 父节点
+ * @param {HTMLElement|Text} child - 子节点
+ *
+ * appendChild函数用于将子节点附加到指定的父节点。
+ */
+export function appendChild(parentInstance, child) {
+ parentInstance.appendChild(child);
+}
+/**
+ * 在指定子节点前插入新的子节点
+ *
+ * @param {HTMLElement} parentInstance - 父节点
+ * @param {HTMLElement|Text} child - 需要插入的新子节点
+ * @param {HTMLElement|Text} beforeChild - 指定的子节点
+ *
+ * insertBefore函数用于在父节点的指定子节点前插入一个新的子节点。
+ */
+export function insertBefore(parentInstance, child, beforeChild) {
+ parentInstance.insertBefore(child, beforeChild);
+}