> 文章列表 > react从render开始与内部执行与实现(一)

react从render开始与内部执行与实现(一)

react从render开始与内部执行与实现(一)

学习笔记react17中render方法内部执行与实现以root节点为例

react-dom中render方法

React.render(<App/>, document.getElementById('root'));

在react-dom模块中index.js文件里找到render方法进入ReactDOMLegacy.js模块

export {createPortal,unstable_batchedUpdates,flushSync,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,version,findDOMNode,hydrate,render,unmountComponentAtNode,createRoot,createRoot as unstable_createRoot,createBlockingRoot,createBlockingRoot as unstable_createBlockingRoot,unstable_flushControlled,unstable_scheduleHydration,unstable_runWithPriority,unstable_renderSubtreeIntoContainer,unstable_createPortal,unstable_createEventHandle,unstable_isNewReconciler,
} from './src/client/ReactDOM';

ReactDOMLegacy模块render方法

/* 接受react组件把它转换成真真实的dom元素* @param {*} element 虚拟dom* @param {*} container dom元素/容器元素* @param {*} callback * @returns */
export function render(element: React$Element<any>,container: Container,callback: ?Function,
) {invariant(isValidContainer(container),'Target container is not a DOM element.',);// 将虚拟dom渲染成真实的dom挂载到容器上return legacyRenderSubtreeIntoContainer(null,element,container,false,callback,);
}

legacyRenderSubtreeIntoContainer方法,判断是否有root节点是否存在,没有就创建,同时都会调用updateContainer方法来更新DOM元素,最后通过getPublicRootInstance根节点的实例

/* 接受react组件把它转换成真真实的dom元素* @param {*} parentComponent 渲染的 React 元素所属的父组件* @param {*} children 虚拟dom* @param {*} container 容器元素  document.getElementById('root')* @param {*} forceHydrate 是否为不同的根元素(不同的根元素需要卸载原来的DOM再挂载新的DOM)* @param {*} callback 回调函数* @returns */
function legacyRenderSubtreeIntoContainer(parentComponent: ?React$Component<any, any>,children: ReactNodeList,container: Container,forceHydrate: boolean,callback: ?Function,
) {// 没有根节点,表示还没有挂载let root: RootType = (container._reactRootContainer: any);let fiberRoot;if (!root) {// 没有root节点时创建root节点// 获取容器元素上_reactRootContainer属性  forceHydrate是否在服务端渲染root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate,);fiberRoot = root._internalRoot;if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = getPublicRootInstance(fiberRoot);originalCallback.call(instance);};}// 初始化时不能批量运行unbatchedUpdates(() => {updateContainer(children, fiberRoot, parentComponent, callback);});} else {fiberRoot = root._internalRoot;if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = getPublicRootInstance(fiberRoot);originalCallback.call(instance);};}// 新的虚拟DOM转换成真实的DOM元素并且更新到页面上updateContainer(children, fiberRoot, parentComponent, callback);}// 返回根节点的实例return getPublicRootInstance(fiberRoot);
}

legacyCreateRootFromDOMContainer最终调用createFiberRoot方法返回FiberRoot并初始化更新队列initializeUpdateQueue此处tagexport const LegacyRoot = 0;

export function createFiberRoot(containerInfo: any,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);if (enableSuspenseCallback) {root.hydrationCallbacks = hydrationCallbacks;}// 创建一个未初始化的 HostRootFiber 对象,并将其赋值给 FiberRoot 的 current 属性。// 同时,将 FiberRoot 对象赋值给 HostRootFiber 的 stateNode 属性,以便它们可以互相引用const uninitializedFiber = createHostRootFiber(tag);root.current = uninitializedFiber;uninitializedFiber.stateNode = root;initializeUpdateQueue(uninitializedFiber);return root;
}

updateContainer更新DOM元素

updateContainer 方法用于将一个 React 元素更新到指定的 DOM 容器中,并返回更新任务的优先级即一个Lane 对象。Lane 是 React 中用于调度更新任务的概念,不同的任务会被分配到不同的 Lane 中,以保证任务的优先级和执行顺序。

/* 接受react组件把它转换成真真实的dom元素* @param {*} children 虚拟dom* @param {*} container 容器元素  document.getElementById('root')  legacyCreateRootFromDOMContainer返回实例* @param {*} 渲染的 React 元素所属的父组件* @param {*} callback 回调函数* @returns */
export function updateContainer(element: ReactNodeList,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,callback: ?Function,
): Lane {// 获取容器的 Fiber 对象// 即方法创建的 const uninitializedFiber = createHostRootFiber(tag); root.current = uninitializedFiber;const current = container.current;// 获取当前时间const eventTime = requestEventTime();// 根据优先级获取更新任务对应的 Laneconst lane = requestUpdateLane(current);// 标记渲染任务已经被调度,用于性能分析if (enableSchedulingProfiler) {markRenderScheduled(lane);}// 获取更新任务的上下文const context = getContextForSubtree(parentComponent);if (container.context === null) {container.context = context;} else {container.pendingContext = context;}// 创建更新对象const update = createUpdate(eventTime, lane);// Caution: React DevTools currently depends on this property 将要更新的 React 元素设置到更新对象中// being called "element".update.payload = {element};// 将更新对象加入到任务队列中,等待执行。在任务执行时,会遍历 Fiber 树,比较新旧两棵树的差异,然后根据差异进行相应的更新操作enqueueUpdate(current, update);// 调度更新任务 current有stateNode字段,stateNode值为root 即FiberRootNode(containerInfo)创建scheduleUpdateOnFiber(current, lane, eventTime);return lane;
}

scheduleRootUpdate触发Fiber树的更新

export function scheduleUpdateOnFiber(fiber: Fiber,lane: Lane,eventTime: number,
) {// 用于检查是否有嵌套的更新checkForNestedUpdates();// 将更新标记从Fiber节点向上移动到其根节点。这是为了确保React可以找到要更新的根节点,并对其进行更新。如果没有找到根节点,则函数返nconst root = markUpdateLaneFromFiberToRoot(fiber, lane);if (root === null) {return null;}// 用于标记根节点已更新,并将更新的优先级和事件时间记录在根节点上markRootUpdated(root, lane, eventTime);// 判断要更新的根节点是当前正在进行的工作的根节点if (root === workInProgressRoot) {if (deferRenderPhaseUpdateToNextBatch ||(executionContext & RenderContext) === NoContext) {// deferRenderPhaseUpdateToNextBatch为真,说明当前的更新任务需要推迟到下一个batch中执行// mergeLanes函数合并更新的优先级,可以保证更新被正确地排序和处理workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes,lane,);}// workInProgressRootExitStatus为RootSuspendedWithDelay时,将root标记为暂停状态// 具体来说,当渲染一个fiber树时,如果该fiber树被挂起(如等待异步数据)则会将// workInProgressRootExitStatus标记为RootSuspendedWithDelay。if (workInProgressRootExitStatus === RootSuspendedWithDelay) {// 用于将根节点标记为已经挂起,不会尝试渲染该根节点的任何更新,直到挂起的数据可用// markRootSuspended函数有两个参数:root(FiberNode对象)和suspended lanes(表示当前已挂起的lane的位掩码)。它将root的// pendingLanes设置为suspended lanes,并将其到期时间设置为Sync,这表示应尽快处理更新。markRootSuspended(root, workInProgressRootRenderLanes);}}const priorityLevel = getCurrentPriorityLevel();if (lane === SyncLane) {if ((executionContext & LegacyUnbatchedContext) !== NoContext &&(executionContext & (RenderContext | CommitContext)) === NoContext) {// 当前执行上下文为 LegacyUnbatchedContext,且不在渲染或提交上下文中,则直接执行同步任务schedulePendingInteractions(root, lane);performSyncWorkOnRoot(root);} else {ensureRootIsScheduled(root, eventTime);schedulePendingInteractions(root, lane);if (executionContext === NoContext) {resetRenderTimer();flushSyncCallbackQueue();}}} else {// 如果是异步更新模式,则提交异步更新任务if ((executionContext & DiscreteEventContext) !== NoContext &&(priorityLevel === UserBlockingSchedulerPriority ||priorityLevel === ImmediateSchedulerPriority)) {if (rootsWithPendingDiscreteUpdates === null) {rootsWithPendingDiscreteUpdates = new Set([root]);} else {rootsWithPendingDiscreteUpdates.add(root);}}// 上面updateContainer方法中 container.current// 根据优先级根节点更新ensureRootIsScheduled(root, eventTime);schedulePendingInteractions(root, lane);}mostRecentlyUpdatedRoot = root;
}

ensureRootIsScheduled根节点跟新任务

该方法检查现有任务的优先级是否与根节点下一级工作的优先级相同,这个函数在更新时或任务结束之前被调用

function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {const existingCallbackNode = root.callbackNode;// 检测lanes是否被占用,被占用标记过期markStarvedLanesAsExpired(root, currentTime);// 获取任务优先级const nextLanes = getNextLanes(root,root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,);const newCallbackPriority = returnNextLanesPriority();// 没任务进行释放if (nextLanes === NoLanes) {if (existingCallbackNode !== null) {cancelCallback(existingCallbackNode);root.callbackNode = null;root.callbackPriority = NoLanePriority;}return;}// Check if there's an existing task. We may be able to reuse it.if (existingCallbackNode !== null) {const existingCallbackPriority = root.callbackPriority;if (existingCallbackPriority === newCallbackPriority) {// The priority hasn't changed. We can reuse the existing task. Exit.return;}// 任务优先级改变了 开启新任务cancelCallback(existingCallbackNode);}// 新任务let newCallbackNode;if (newCallbackPriority === SyncLanePriority) {//  指定任务类型 export const SyncLanePriority: LanePriority = 15;newCallbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root),);} else if (newCallbackPriority === SyncBatchedLanePriority) {// 任务类型  export const SyncBatchedLanePriority: LanePriority = 14;// ImmediateSchedulerPriority 最高优先级newCallbackNode = scheduleCallback(ImmediateSchedulerPriority,performSyncWorkOnRoot.bind(null, root),);} else {// 获取对应的优先级const schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority,);newCallbackNode = scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root),);}root.callbackPriority = newCallbackPriority;root.callbackNode = newCallbackNode;
}

performConcurrentWorkOnRoot为并发入口,通过它调度程序(Scheduler)执行任务,每一个更新和渲染任务都是一个并发任务,并由Scheduler管理