06.beginWork
同学们好,这一小节我们就来实现render函数中beginWork部分的逻辑。
packages/react-reconciler/src/ReactFiberWorkLoop.js
diff
import { scheduleCallback } from "scheduler";
+ import { createWorkInProgress } from "./ReactFiber";
+ import { beginWork } from "./ReactFiberBeginWork";
+ 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) {
- console.log('正式开启beginwork、completeWork、commitWork')
+ renderRootSync(root);
+ const finishedWork = root.current.alternate;
+ root.finishedWork = finishedWork;
+ // commitRoot(root);
}
+/**
+ * 准备一个新的工作栈。
+ * @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;
+ workInProgress = null //临时加上,防止死循环
+ if (next === null) {
+ completeUnitOfWork(unitOfWork);
+ } else {
+ workInProgress = next;
+ }
+}
+/**
+ * 完成一个工作单元。
+ * @param {*} unitOfWork - 工作单元。
+ */
+function completeUnitOfWork(unitOfWork) {
+ console.log('开启comleteWork阶段')
+}packages/react-reconciler/src/ReactFiber.js
diff
// 导入React中的一些工作标签和标记
-import { HostRoot} from "./ReactWorkTags";
+import { HostComponent, HostRoot, IndeterminateComponent, HostText } from "./ReactWorkTags";
import { NoFlags } from "./ReactFiberFlags";
/**
* 构造函数,用于创建一个新的Fiber节点
* @param {number} tag - fiber的类型,如函数组件、类组件、原生组件、根元素等
* @param {*} pendingProps - 新属性,等待处理或者说生效的属性
* @param {*} key - 唯一标识
*/
export function FiberNode(tag, pendingProps, key) {
this.tag = tag;
this.key = key;
this.type = null;
this.stateNode = null;
this.return = null;
this.child = null;
this.sibling = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.memoizedState = null;
this.updateQueue = null;
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.alternate = null;
this.index = 0;
}
/**
* 用于创建新的Fiber节点
* @param {number} tag - fiber的类型
* @param {*} pendingProps - 新属性
* @param {*} key - 唯一标识
* @returns {FiberNode} 新的Fiber节点
*/
export function createFiber(tag, pendingProps, key) {
return new FiberNode(tag, pendingProps, key);
}
/**
* 创建新的HostRoot类型的Fiber节点
* @returns {FiberNode} 新的HostRoot类型的Fiber节点
*/
export function createHostRootFiber() {
return createFiber(HostRoot, null, null);
}
+/**
+ * 基于旧的Fiber节点和新的属性创建一个新的Fiber节点
+ * @param {FiberNode} current - 旧的Fiber节点
+ * @param {*} pendingProps - 新的属性
+ * @returns {FiberNode} 新的Fiber节点
+ */
+export function createWorkInProgress(current, pendingProps) {
+ let workInProgress = current.alternate;
+ if (workInProgress === null) {
+ workInProgress = createFiber(current.tag, pendingProps, current.key);
+ workInProgress.type = current.type;
+ workInProgress.stateNode = current.stateNode;
+ workInProgress.alternate = current;
+ current.alternate = workInProgress;
+ } else {
+ workInProgress.pendingProps = pendingProps;
+ workInProgress.type = current.type;
+ workInProgress.flags = NoFlags;
+ workInProgress.subtreeFlags = NoFlags;
+ }
+ workInProgress.child = current.child;
+ workInProgress.memoizedProps = current.memoizedProps;
+ workInProgress.memoizedState = current.memoizedState;
+ workInProgress.updateQueue = current.updateQueue;
+ workInProgress.sibling = current.sibling;
+ workInProgress.index = current.index;
+ return workInProgress;
+}
+/**
+ * 从虚拟DOM创建新的Fiber节点
+ * @param {*} element - 虚拟DOM元素
+ * @returns {FiberNode} 新的Fiber节点
+ */
+export function createFiberFromElement(element) {
+ const { type, key, props: pendingProps } = element;
+ return createFiberFromTypeAndProps(type, key, pendingProps);
+}
+/**
+ * 从类型和属性创建新的Fiber节点
+ * @param {*} type - Fiber节点的类型
+ * @param {*} key - 唯一标识
+ * @param {*} pendingProps - 新的属性
+ * @returns {FiberNode} 新的Fiber节点
+ */
+function createFiberFromTypeAndProps(type, key, pendingProps) {
+ let tag = IndeterminateComponent;
+ if (typeof type === "string") {
+ tag = HostComponent;
+ }
+ const fiber = createFiber(tag, pendingProps, key);
+ fiber.type = type;
+ return fiber;
+}
+/**
+ * 创建一个新的文本类型的Fiber节点
+ * @param {*} content - 文本内容
+ * @returns {FiberNode} 新的文本类型的Fiber节点
+ */
+export function createFiberFromText(content) {
+ return createFiber(HostText, content, null);
+}packages/react-reconciler/src/ReactFiberBeginWork.js
js
import { HostComponent, HostRoot, HostText } from "./ReactWorkTags";
import { processUpdateQueue } from "./ReactFiberClassUpdateQueue";
import { mountChildFibers, reconcileChildFibers } from "./ReactChildFiber";
import { shouldSetTextContent } from "react-dom-bindings/src/client/ReactDOMHostConfig";
/**
* 根据新的虚拟DOM生成新的Fiber链表
* @param {FiberNode} current - 老的父Fiber节点
* @param {FiberNode} workInProgress - 新的Fiber节点
* @param {*} nextChildren - 新的子虚拟DOM
*/
function reconcileChildren(current, workInProgress, nextChildren) {
if (current === null) {
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren);
} else {
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren);
}
}
/**
* 更新HostRoot类型的Fiber节点
* @param {FiberNode} current - 老的Fiber节点
* @param {FiberNode} workInProgress - 新的Fiber节点
* @returns {FiberNode} 新的子Fiber节点
*/
function updateHostRoot(current, workInProgress) {
processUpdateQueue(workInProgress);
const nextState = workInProgress.memoizedState;
const nextChildren = nextState.element;
reconcileChildren(current, workInProgress, nextChildren);
return workInProgress.child;
}
/**
* 更新原生组件的Fiber节点并构建子Fiber链表
* @param {FiberNode} current - 老的Fiber节点
* @param {FiberNode} workInProgress - 新的Fiber节点
* @returns {FiberNode} 新的子Fiber节点
*/
function updateHostComponent(current, workInProgress) {
const { type } = workInProgress;
const nextProps = workInProgress.pendingProps;
let nextChildren = nextProps.children;
const isDirectTextChild = shouldSetTextContent(type, nextProps);
if (isDirectTextChild) {
nextChildren = null;
}
reconcileChildren(current, workInProgress, nextChildren);
return workInProgress.child;
}
/**
* 开始根据新的虚拟DOM构建新的Fiber子链表
* @param {FiberNode} current - 老的Fiber节点
* @param {FiberNode} workInProgress - 新的Fiber节点
* @returns {FiberNode|null} 新的子Fiber节点或者null
*/
export function beginWork(current, workInProgress) {
switch (workInProgress.tag) {
case HostRoot:
return updateHostRoot(current, workInProgress);
case HostComponent:
return updateHostComponent(current, workInProgress);
case HostText:
return null;
default:
return null;
}
}packages/react-dom-bindings/src/client/ReactDOMHostConfig.js
js
/**
* 判断是否需要设置文本内容
*
* @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";
}packages/react-reconciler/src/ReactFiberClassUpdateQueue.js
diff
import { markUpdateLaneFromFiberToRoot } from "./ReactFiberConcurrentUpdates";
+import assign from "shared/assign";
// 定义状态更新的类型标签
export const UpdateState = 0;
/**
* 初始化fiber节点的更新队列
* @param {FiberNode} fiber - 需要初始化更新队列的fiber节点
*/
export function initialUpdateQueue(fiber) {
const queue = {
shared: {
pending: null, // 创建一个新的更新队列,其中pending是一个循环链表
},
};
fiber.updateQueue = queue;
}
/**
* 创建一个状态更新对象
* @returns {Update} 更新对象
*/
export function createUpdate() {
const update = { tag: UpdateState };
return update;
}
/**
* 将更新对象添加到fiber节点的更新队列中
* @param {FiberNode} fiber - 需要添加更新的fiber节点
* @param {Update} update - 待添加的更新对象
* @returns {FiberNode} fiber根节点
*/
export function enqueueUpdate(fiber, update) {
const updateQueue = fiber.updateQueue;
const pending = updateQueue.shared.pending;
// 如果pending为空,则让update自引用形成一个循环链表
if (pending === null) {
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
// pending始终指向最后一个更新对象,形成一个单向循环链表
updateQueue.shared.pending = update;
return markUpdateLaneFromFiberToRoot(fiber);
}
+/**
+ * 根据老状态和更新队列中的更新计算最新的状态
+ * @param {FiberNode} workInProgress - 需要计算新状态的fiber节点
+ */
+export function processUpdateQueue(workInProgress) {
+ const queue = workInProgress.updateQueue;
+ const pendingQueue = queue.shared.pending;
+
+ // 如果有更新,则清空更新队列并开始计算新的状态
+ if (pendingQueue !== null) {
+ queue.shared.pending = null;
+ const lastPendingUpdate = pendingQueue;
+ const firstPendingUpdate = lastPendingUpdate.next;
+
+ // 把更新链表剪开,变成一个单链表
+ lastPendingUpdate.next = null;
+ let newState = workInProgress.memoizedState;
+ let update = firstPendingUpdate;
+
+ // 遍历更新队列,根据老状态和更新对象计算新状态
+ while (update) {
+ newState = getStateFromUpdate(update, newState);
+ update = update.next;
+ }
+
+ // 更新fiber节点的memoizedState
+ workInProgress.memoizedState = newState;
+ }
+}
+/**
+ * 根据老状态和更新对象计算新状态
+ * @param {Update} update - 更新对象
+ * @param {*} prevState - 老状态
+ * @returns {*} 新状态
+ */
+function getStateFromUpdate(update, prevState) {
+ switch (update.tag) {
+ case UpdateState:
+ const { payload } = update;
+ // 合并prevState和payload为新状态
+ return assign({}, prevState, payload);
+ }
+}packages/react-reconciler/src/ReactChildFiber.js
js
import { REACT_ELEMENT_TYPE } from "shared/ReactSymbols";
import { createFiberFromElement, createFiberFromText } from "./ReactFiber";
import { Placement } from "./ReactFiberFlags";
import isArray from "shared/isArray";
/**
* 创建Child Reconciler的函数
*
* @param {boolean} shouldTrackSideEffects - 是否需要跟踪副作用
* @return {function} reconcileChildFibers - 用于处理子fiber的函数
*
* 这个函数会根据传入的shouldTrackSideEffects参数返回一个函数reconcileChildFibers,
* reconcileChildFibers函数可以根据新旧Fiber进行比较并返回处理结果。
*/
function createChildReconciler(shouldTrackSideEffects) {
/**
* 将新创建的元素转换为fiber
*
* @param {Fiber} returnFiber - 新的父Fiber
* @param {Fiber} currentFirstFiber - 老fiber第一个子fiber
* @param {object} element - 新的子虚拟DOM元素
* @return {Fiber} created - 返回新创建的Fiber
*/
function reconcileSingleElement(returnFiber, currentFirstFiber, element) {
const created = createFiberFromElement(element);
created.return = returnFiber;
return created;
}
/**
* 设置副作用
*
* @param {Fiber} newFiber - 新创建的Fiber
* @return {Fiber} newFiber - 返回新创建的Fiber
*/
function placeSingleChild(newFiber) {
if (shouldTrackSideEffects) {
newFiber.flags |= Placement;
}
return newFiber;
}
/**
* 根据新的子节点创建Fiber
*
* @param {Fiber} returnFiber - 新的父Fiber
* @param {object} newChild - 新的子节点
* @return {Fiber | null} created - 返回新创建的Fiber,或null
*/
function createChild(returnFiber, newChild) {
if ((typeof newChild === "string" && newChild !== "") || typeof newChild === "number") {
const created = createFiberFromText(`${newChild}`);
created.return = returnFiber;
return created;
}
if (typeof newChild === "object" && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: {
const created = createFiberFromElement(newChild);
created.return = returnFiber;
return created;
}
default:
break;
}
}
return null;
}
/**
* 为新创建的Fiber设置索引,并在必要时设置副作用
*
* @param {Fiber} newFiber - 新创建的Fiber
* @param {number} newIdx - 新的索引
*/
function placeChild(newFiber, newIdx) {
newFiber.index = newIdx;
if (shouldTrackSideEffects) {
newFiber.flags |= Placement;
}
}
/**
* 将新的子节点数组与旧的子Fiber进行比较,并返回新的子Fiber
*
* @param {Fiber} returnFiber - 新的父Fiber
* @param {Fiber} currentFirstFiber - 老fiber第一个子fiber
* @param {Array} newChildren - 新的子节点数组
* @return {Fiber} resultingFirstChild - 返回的新的子Fiber
*/
function reconcileChildrenArray(returnFiber, currentFirstFiber, newChildren) {
let resultingFirstChild = null;
let previousNewFiber = null;
let newIdx = 0;
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = createChild(returnFiber, newChildren[newIdx]);
if (newFiber === null) continue;
placeChild(newFiber, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
return resultingFirstChild;
}
/**
* 比较子Fibers
*
* @param {Fiber} returnFiber - 新的父Fiber
* @param {Fiber} currentFirstFiber - 老fiber第一个子fiber
* @param {object} newChild - 新的子虚拟DOM
* @return {Fiber | null} result - 返回的新的子Fiber,或null
*/
function reconcileChildFibers(returnFiber, currentFirstFiber, newChild) {
if (typeof newChild === "object" && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstFiber, newChild));
default:
break;
}
}
if (isArray(newChild)) {
return reconcileChildrenArray(returnFiber, currentFirstFiber, newChild);
}
return null;
}
return reconcileChildFibers;
}
//有老父fiber更新的时候用这个
export const reconcileChildFibers = createChildReconciler(true);
//如果没有老父fiber,初次挂载的时候用这个
export const mountChildFibers = createChildReconciler(false);packages/shared/assign.js
js
const { assign } = Object;
export default assign;packages/shared/isArray.js
js
const { isArray } = Array;
export default isArray;