Skip to content

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;

基于 VitePress 构建