当前位置: 首页 > news >正文

嘉兴做企业网站的公司绵阳免费网站建设

嘉兴做企业网站的公司,绵阳免费网站建设,app设计开发要多少钱,宁波网络推广公司价格本章将讲解 react 的核心阶段之一 —— render阶段#xff0c;我们将探究以下部分内容的源码#xff1a; 更新任务的触发更新任务的创建reconciler 过程同步和异步遍历及执行任务scheduler 是如何实现帧空闲时间调度任务以及中断任务的 触发更新 触发更新的方式主要有以下几…本章将讲解 react 的核心阶段之一 —— render阶段我们将探究以下部分内容的源码 更新任务的触发更新任务的创建reconciler 过程同步和异步遍历及执行任务scheduler 是如何实现帧空闲时间调度任务以及中断任务的 触发更新 触发更新的方式主要有以下几种ReactDOM.render、setState、forUpdate 以及 hooks 中的 useState 等关于 hooks 的我们后面再详细讲解这里先关注前三种情况。 ReactDOM.render ReactDOM.render 作为 react 应用程序的入口函数在页面首次渲染时便会触发页面 dom 的首次创建也属于触发 react 更新的一种情况。 首先调用 legacyRenderSubtreeIntoContainer 函数校验根节点 root 是否存在若不存在调用 legacyCreateRootFromDOMContainer 创建根节点 root、rootFiber 和 fiberRoot 并绑定它们之间的引用关系然后调用 updateContainer 去非批量执行后面的更新流程若存在直接调用 updateContainer 去批量执行后面的更新流程 // packages/react-dom/src/client/ReactDOMLegacy.jsfunction legacyRenderSubtreeIntoContainer(parentComponent: ?React$Componentany, any, children: ReactNodeList, container: Container, forceHydrate: boolean, callback: ?Function, ) {// ...let root: RootType (container._reactRootContainer: any);let fiberRoot;if (!root) {// 首次渲染时根节点不存在// 通过 legacyCreateRootFromDOMContainer 创建根节点、fiberRoot 和 rootFiberroot 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);};}// 批量执行更新流程updateContainer(children, fiberRoot, parentComponent, callback);}return getPublicRootInstance(fiberRoot); }updateContainer 函数中主要做了以下几件事情 requestEventTime获取更新触发的时间requestUpdateLane获取当前任务优先级createUpdate创建更新enqueueUpdate将任务推进更新队列scheduleUpdateOnFiber调度更新 关于这几个函数稍后会详细讲到 // packages/react-dom/src/client/ReactDOMLegacy.jsexport function updateContainer(element: ReactNodeList, container: OpaqueRoot, parentComponent: ?React$Componentany, any, callback: ?Function, ): Lane {// ...const current container.current;const eventTime requestEventTime(); // 获取更新触发的时间// ...const 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); // 创建更新任务update.payload {element};callback callback undefined ? null : callback;if (callback ! null) {// ...update.callback callback;}enqueueUpdate(current, update); // 将任务推入更新队列scheduleUpdateOnFiber(current, lane, eventTime); // schedule 进行调度return lane; }setState setState 时类组件中我们最常用的修改状态的方法状态修改会触发更新流程。 class 组件在原型链上定义了 setState 方法其调用了触发器 updater 上的 enqueueSetState 方法 // packages/react/src/ReactBaseClasses.jsComponent.prototype.setState function(partialState, callback) {invariant(typeof partialState object ||typeof partialState function ||partialState null,setState(...): takes an object of state variables to update or a function which returns an object of state variables.,);this.updater.enqueueSetState(this, partialState, callback, setState); };然后我们再来看以下 updater 上定义的 enqueueSetState 方法一看到这我们就了然了和 updateContainer 方法中做的事情几乎一模一样都是触发后续的更新调度。 // packages/react-reconciler/src/ReactFiberClassComponent.old.jsconst classComponentUpdater {isMounted,enqueueSetState(inst, payload, callback) {const fiber getInstance(inst);const eventTime requestEventTime(); // 获取更新触发的时间const lane requestUpdateLane(fiber); // 获取任务优先级const update createUpdate(eventTime, lane); // 创建更新任务update.payload payload;if (callback ! undefined callback ! null) {if (__DEV__) {warnOnInvalidCallback(callback, setState);}update.callback callback;}enqueueUpdate(fiber, update); // 将任务推入更新队列scheduleUpdateOnFiber(fiber, lane, eventTime); // schedule 进行调度// ...if (enableSchedulingProfiler) {markStateUpdateScheduled(fiber, lane);}},// ... };forceUpdate forceUpdate 的流程与 setState 几乎一模一样 同样其调用了触发器 updater 上的 enqueueForceUpdate 方法enqueueForceUpdate 方法也同样是触发了一系列的更新流程相关参考视频讲解进入学习 reconciler/src/ReactFiberClassComponent.old.jsconst classComponentUpdater {isMounted,// ...enqueueForceUpdate(inst, callback) {const fiber getInstance(inst);const eventTime requestEventTime(); // 获取更新触发的时间const lane requestUpdateLane(fiber); // 获取任务优先级const update createUpdate(eventTime, lane); // 创建更新update.tag ForceUpdate;if (callback ! undefined callback ! null) {if (__DEV__) {warnOnInvalidCallback(callback, forceUpdate);}update.callback callback;}enqueueUpdate(fiber, update); // 将任务推进更新队列scheduleUpdateOnFiber(fiber, lane, eventTime); // 触发更新调度// ...if (enableSchedulingProfiler) {markForceUpdateScheduled(fiber, lane);}}, };创建更新任务 可以发现上述的三种触发更新的动作最后殊途同归都会走上述流程图中从 requestEventTime 到 scheduleUpdateOnFiber 这一流程去创建更新任务先我们详细看下更新任务是如何创建的。 获取更新触发时间 前面的文章中我们讲到过react 执行更新过程中会将更新任务拆解每一帧优先执行高优先级的任务从而保证用户体验的流畅。那么即使对于同样优先级的任务在任务多的情况下该优先执行哪一些呢 react 通过 requestEventTime 方法去创建一个 currentEventTime用于标识更新任务触发的时间对于相同时间的任务会批量去执行。同样优先级的任务currentEventTime 值越小就会越早执行。 我们看一下 requestEventTime 方法的实现 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsexport function requestEventTime() {if ((executionContext (RenderContext | CommitContext)) ! NoContext) {// 在 react 执行过程中直接返回当前时间return now();}// 如果不在 react 执行过程中if (currentEventTime ! NoTimestamp) {// 正在执行浏览器事件返回上次的 currentEventTimereturn currentEventTime;}// react 中断后首次更新计算新的 currentEventTimecurrentEventTime now();return currentEventTime; }在这个方法中(executionContext (RenderContext | CommitContext) 做了二进制运算RenderContext 代表着 react 正在计算更新CommitContext 代表着 react 正在提交更新。所以这句话是判断当前 react 是否处在计算或者提交更新的阶段如果是则直接返回 now()。 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsexport const NoContext /* */ 0b0000000; const BatchedContext /* */ 0b0000001; const EventContext /* */ 0b0000010; const DiscreteEventContext /* */ 0b0000100; const LegacyUnbatchedContext /* */ 0b0001000; const RenderContext /* */ 0b0010000; const CommitContext /* */ 0b0100000; export const RetryAfterError /* */ 0b1000000;let executionContext: ExecutionContext NoContext;再来看一下 now 的代码这里的意思时当前后的更新任务时间差小于 10ms 时直接采用上次的 Scheduler_now这样可以抹平 10ms 内更新任务的时间差 有利于批量更新 // packages/react-reconciler/src/SchedulerWithReactIntegration.old.jsexport const now initialTimeMs 10000 ? Scheduler_now : () Scheduler_now() - initialTimeMs;综上所述requestEvent 做的事情如下 在 react 的 render 和 commit 阶段我们直接获取更新任务的触发时间并抹平相差 10ms 以内的更新任务以便于批量执行。当 currentEventTime 不等于 NoTimestamp 时则判断其正在执行浏览器事件react 想要同样优先级的更新任务保持相同的时间所以直接返回上次的 currentEventTime如果是 react 上次中断之后的首次更新那么给 currentEventTime 赋一个新的值 划分更新任务优先级 说完了相同优先级任务的触发时间那么任务的优先级又是如何划分的呢这里就要提到 requestUpdateLane我们来看一下其源码 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsexport function requestUpdateLane(fiber: Fiber): Lane {// ...// 根据记录下的事件的优先级获取任务调度的优先级const schedulerPriority getCurrentPriorityLevel();// ...let lane;if ((executionContext DiscreteEventContext) ! NoContext schedulerPriority UserBlockingSchedulerPriority) {// 如果是用户阻塞级别的事件则通过InputDiscreteLanePriority 计算更新的优先级 lanelane findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes);} else {// 否则依据事件的优先级计算 schedulerLanePriorityconst schedulerLanePriority schedulerPriorityToLanePriority(schedulerPriority,);if (decoupleUpdatePriorityFromScheduler) {const currentUpdateLanePriority getCurrentUpdateLanePriority();// 根据计算得到的 schedulerLanePriority计算更新的优先级 lanelane findUpdateLane(schedulerLanePriority, currentEventWipLanes);}return lane; }它首先找出会通过 getCurrentPriorityLevel 方法根据 Scheduler 中记录的事件优先级获取任务调度的优先级 schedulerPriority。然后通过 findUpdateLane 方法计算得出 lane作为更新过程中的优先级。 findUpdateLane 这个方法中按照事件的类型匹配不同级别的 lane事件类型的优先级划分如下值越高代表优先级越高 // packages/react-reconciler/src/ReactFiberLane.jsexport const SyncLanePriority: LanePriority 15; export const SyncBatchedLanePriority: LanePriority 14; const InputDiscreteHydrationLanePriority: LanePriority 13; export const InputDiscreteLanePriority: LanePriority 12; const InputContinuousHydrationLanePriority: LanePriority 11; export const InputContinuousLanePriority: LanePriority 10; const DefaultHydrationLanePriority: LanePriority 9; export const DefaultLanePriority: LanePriority 8; const TransitionHydrationPriority: LanePriority 7; export const TransitionPriority: LanePriority 6; const RetryLanePriority: LanePriority 5; const SelectiveHydrationLanePriority: LanePriority 4; const IdleHydrationLanePriority: LanePriority 3; const IdleLanePriority: LanePriority 2; const OffscreenLanePriority: LanePriority 1; export const NoLanePriority: LanePriority 0;创建更新对象 eventTime 和 lane 都创建好了之后就该创建更新了createUpdate 就是基于上面两个方法所创建的 eventTime 和 lane去创建一个更新对象 // packages/react-reconciler/src/ReactUpdateQueue.old.jsexport function createUpdate(eventTime: number, lane: Lane): Update* {const update: Update* {eventTime, // 更新要出发的事件lane, // 优先级tag: UpdateState, // 指定更新的类型0更新 1替换 2强制更新 3捕获性的更新payload: null, // 要更新的内容例如 setState 接收的第一个参数callback: null, // 更新完成后的回调next: null, // 指向下一个更新};return update; }关联 fiber 的更新队列 创建好了 update 对象之后紧接着调用 enqueueUpdate 方法把update 对象放到 关联的 fiber 的 updateQueue 队列之中 // packages/react-reconciler/src/ReactUpdateQueue.old.jsexport function enqueueUpdateState(fiber: Fiber, update: UpdateState) {// 获取当前 fiber 的更新队列const updateQueue fiber.updateQueue;if (updateQueue null) {// 若 updateQueue 为空表示 fiber 还未渲染直接退出return;}const sharedQueue: SharedQueueState (updateQueue: any).shared;const pending sharedQueue.pending;if (pending null) {// pending 为 null 时表示首次更新创建循环列表update.next update;} else {// 将 update 插入到循环列表中update.next pending.next;pending.next update;}sharedQueue.pending update;// ... }reconciler 过程 上面的更新任务创建好了并且关联到了 fiber 上下面就该到了 react render 阶段的核心之一 —— reconciler 阶段。 根据任务类型执行不同更新 reconciler 阶段会协调任务去执行以 scheduleUpdateOnFiber 为入口函数首先会调用 checkForNestedUpdates 方法检查嵌套的更新数量若嵌套数量大于 50 层时被认为是循环更新无限更新。此时会抛出异常避免了例如在类组件 render 函数中调用了 setState 这种死循环的情况。 然后通过 markUpdateLaneFromFiberToRoot 方法向上递归更新 fiber 的 lanelane 的更新很简单就是将当前任务 lane 与之前的 lane 进行二进制或运算叠加。 我们看一下其源码 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsexport function scheduleUpdateOnFiber(fiber: Fiber, lane: Lane, eventTime: number, ) {// 检查是否有循环更新// 避免例如在类组件 render 函数中调用了 setState 这种死循环的情况checkForNestedUpdates();// ...// 自底向上更新 child.fiberLanesconst root markUpdateLaneFromFiberToRoot(fiber, lane);// ...// 标记 root 有更新将 update 的 lane 插入到root.pendingLanes 中markRootUpdated(root, lane, eventTime);if (lane SyncLane) { // 同步任务采用同步渲染if ((executionContext LegacyUnbatchedContext) ! NoContext (executionContext (RenderContext | CommitContext)) NoContext) {// 如果本次是同步更新并且当前还未开始渲染// 表示当前的 js 主线程空闲并且没有 react 任务在执行// ...// 调用 performSyncWorkOnRoot 执行同步更新任务performSyncWorkOnRoot(root);} else {// 如果本次时同步更新但是有 react 任务正在执行// 调用 ensureRootIsScheduled 去复用当前正在执行的任务让其将本次的更新一并执行ensureRootIsScheduled(root, eventTime);schedulePendingInteractions(root, lane);// ...} else {// 如果本次更新是异步任务// ... // 调用 ensureRootIsScheduled 执行可中断更新ensureRootIsScheduled(root, eventTime);schedulePendingInteractions(root, lane);}mostRecentlyUpdatedRoot root; }然后会根据任务类型以及当前线程所处的 react 执行阶段去判断进行何种类型的更新 执行同步更新 当任务的类型为同步任务并且当前的 js 主线程空闲没有正在执行的 react 任务时会通过 performSyncWorkOnRoot(root) 方法开始执行同步任务。 performSyncWorkOnRoot 里面主要做了两件事 renderRootSync 从根节点开始进行同步渲染任务commitRoot 执行 commit 流程 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsfunction performSyncWorkOnRoot(root) {// ...exitStatus renderRootSync(root, lanes);// ...commitRoot(root);// ... }当任务类型为同步类型但是 js 主线程非空闲时。会执行 ensureRootIsScheduled 方法 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsfunction ensureRootIsScheduled(root: FiberRoot, currentTime: number) {// ...// 如果有正在执行的任务if (existingCallbackNode ! null) {const existingCallbackPriority root.callbackPriority;if (existingCallbackPriority newCallbackPriority) {// 任务优先级没改变说明可以复用之前的任务一起执行return;}// 任务优先级改变了说明不能复用。// 取消正在执行的任务重新去调度cancelCallback(existingCallbackNode);}// 进行一个新的调度let newCallbackNode;if (newCallbackPriority SyncLanePriority) {// 如果是同步任务优先级执行 performSyncWorkOnRootnewCallbackNode scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root),);} else if (newCallbackPriority SyncBatchedLanePriority) {// 如果是批量同步任务优先级执行 performSyncWorkOnRootnewCallbackNode scheduleCallback(ImmediateSchedulerPriority,performSyncWorkOnRoot.bind(null, root),);} else {// ...// 如果不是批量同步任务优先级执行 performConcurrentWorkOnRootnewCallbackNode scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root),);}// ... }ensureRootIsScheduled 方法中会先看加入了新的任务后根节点任务优先级是否有变更如果无变更说明新的任务会被当前的 schedule 一同执行如果有变更则创建新的 schedule然后也是调用performSyncWorkOnRoot(root) 方法开始执行同步任务。 执行可中断更新 当任务的类型不是同步类型时react 也会执行 ensureRootIsScheduled 方法因为是异步任务最终会执行 performConcurrentWorkOnRoot 方法去进行可中断的更新下面会详细讲到。 workLoop 同步 以同步更新为例performSyncWorkOnRoot 会经过以下流程performSyncWorkOnRoot —— renderRootSync —— workLoopSync。 workLoopSync 中只要 workInProgressworkInProgress fiber 树中新创建的 fiber 节点 不为 null就会一直循环执行 performUnitOfWork 函数。 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsfunction workLoopSync() {while (workInProgress ! null) {performUnitOfWork(workInProgress);} }可中断 可中断模式下performConcurrentWorkOnRoot 会执行以下过程performConcurrentWorkOnRoot —— renderRootConcurrent —— workLoopConcurrent。 相比于 workLoopSync, workLoopConcurrent 在每一次对 workInProgress 执行 performUnitOfWork 前会先判断以下 shouldYield() 的值。若为 false 则继续执行若为 true 则中断执行。 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsfunction workLoopConcurrent() {while (workInProgress ! null !shouldYield()) {performUnitOfWork(workInProgress);} }performUnitOfWork 最终无论是同步执行任务还是可中断地执行任务都会进入 performUnitOfWork 函数中。 performUnitOfWork 中会以 fiber 作为单元进行协调过程。每次 beginWork 执行后都会更新 workIngProgress从而响应了上面 workLoop 的循环。 直至 fiber 树便利完成后workInProgress 此时置为 null执行 completeUnitOfWork 函数。 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsfunction performUnitOfWork(unitOfWork: Fiber): void {// ...const current unitOfWork.alternate;// ...let next;if (enableProfilerTimer (unitOfWork.mode ProfileMode) ! NoMode) {// ...next beginWork(current, unitOfWork, subtreeRenderLanes);} else {next beginWork(current, unitOfWork, subtreeRenderLanes);}// ...if (next null) {completeUnitOfWork(unitOfWork);} else {workInProgress next;}ReactCurrentOwner.current null; }beginWork beginWork 是根据当前执行环境封装调用了 originalBeginWork 函数 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jslet beginWork; if (__DEV__ replayFailedUnitOfWorkWithInvokeGuardedCallback) {beginWork (current, unitOfWork, lanes) {// ...try {return originalBeginWork(current, unitOfWork, lanes);} catch (originalError) {// ...}}; } else {beginWork originalBeginWork; }originalBeginWork 中会根据 workInProgress 的 tag 属性执行不同类型的 react 元素的更新函数。但是他们都大同小异不论是 tag 是何种类型更新函数最终都会去调用 reconcileChildren 函数。 // packages/react-reconciler/src/ReactFiberBeginWork.old.jsfunction beginWork(current: Fiber | null, workInProgress: Fiber, renderLanes: Lanes, ): Fiber | null {const updateLanes workInProgress.lanes;workInProgress.lanes NoLanes;// 针对 workInProgress 的tag执行相应的更新switch (workInProgress.tag) {// ...case HostRoot:return updateHostRoot(current, workInProgress, renderLanes);case HostComponent:return updateHostComponent(current, workInProgress, renderLanes);// ...}// ... }以 updateHostRoot 为例根据根 fiber 是否存在去执行 mountChildFibers 或者 reconcileChildren // packages/react-reconciler/src/ReactFiberBeginWork.old.jsfunction updateHostRoot(current, workInProgress, renderLanes) {// ...const root: FiberRoot workInProgress.stateNode;if (root.hydrate enterHydrationState(workInProgress)) {// 若根 fiber 不存在说明是首次渲染调用 mountChildFibers// ...const child mountChildFibers(workInProgress,null,nextChildren,renderLanes,);workInProgress.child child;} else {// 若根 fiber 存在调用 reconcileChildrenreconcileChildren(current, workInProgress, nextChildren, renderLanes);resetHydrationState();}return workInProgress.child; }reconcileChildren 做的事情就是 react 的另一核心之一 —— diff 过程在下一篇文章中会详细讲。 completeUnitOfWork 当 workInProgress 为 null 时也就是当前任务的 fiber 树遍历完之后就进入到了 completeUnitOfWork 函数。 经过了 beginWork 操作workInProgress 节点已经被打上了flags 副作用标签。completeUnitOfWork 方法中主要是逐层收集 effects 链最终收集到 root 上供接下来的commit阶段使用。 completeUnitOfWork 结束后render 阶段便结束了后面就到了 commit 阶段。 // packages/react-reconciler/src/ReactFiberWorkLoop.old.js function completeUnitOfWork(unitOfWork: Fiber): void {let completedWork unitOfWork;do {// ... // 对节点进行completeWork生成DOM更新props绑定事件next completeWork(current, completedWork, subtreeRenderLanes);if (returnFiber ! null (returnFiber.flags Incomplete) NoFlags) {// 将当前节点的 effectList 并入到父节点的 effectListif (returnFiber.firstEffect null) {returnFiber.firstEffect completedWork.firstEffect;}if (completedWork.lastEffect ! null) {if (returnFiber.lastEffect ! null) {returnFiber.lastEffect.nextEffect completedWork.firstEffect;}returnFiber.lastEffect completedWork.lastEffect;}// 将自身添加到 effectList 链添加时跳过 NoWork 和 PerformedWork的 flags因为真正的 commit 时用不到const flags completedWork.flags;if (flags PerformedWork) {if (returnFiber.lastEffect ! null) {returnFiber.lastEffect.nextEffect completedWork;} else {returnFiber.firstEffect completedWork;}returnFiber.lastEffect completedWork;}}} while (completedWork ! null);// ... }scheduler 实现帧空闲调度任务 刚刚上面说到了在执行可中断的更新时浏览器会在每一帧空闲时刻去执行 react 更新任务那么空闲时刻去执行是如何实现的呢我们很容易联想到一个 api —— requestIdleCallback。但由于 requestIdleCallback 的兼容性问题以及 react 对应部分高优先级任务可能牺牲部分帧的需要react 通过自己实现了类似的功能代替了 requestIdleCallback。 我们上面讲到执行可中断更新时performConcurrentWorkOnRoot 函数时通过 scheduleCallback 包裹起来的 scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root), );scheduleCallback 函数是引用了 packages/scheduler/src/Scheduler.js 路径下的 unstable_scheduleCallback 函数我们来看一下这个函数它会去按计划插入调度任务 // packages/scheduler/src/Scheduler.jsfunction unstable_scheduleCallback(priorityLevel, callback, options) {// ...if (startTime currentTime) {// 当前任务已超时插入超时队列// ...} else {// 任务未超时插入调度任务队列newTask.sortIndex expirationTime;push(taskQueue, newTask);// 符合更新调度执行的标志if (!isHostCallbackScheduled !isPerformingWork) {isHostCallbackScheduled true;// requestHostCallback 调度任务requestHostCallback(flushWork);}}return newTask; }将任务插入了调度队列之后会通过 requestHostCallback 函数去调度任务。 react 通过 new MessageChannel() 创建了消息通道当发现 js 线程空闲时通过 postMessage 通知 scheduler 开始调度。然后 react 接收到调度开始的通知时就通过 performWorkUntilDeadline 函数去更新当前帧的结束时间以及执行任务。从而实现了帧空闲时间的任务调度。 // packages/scheduler/src/forks/SchedulerHostConfig.default.js// 获取当前设备每帧的时长 forceFrameRate function(fps) {// ...if (fps 0) {yieldInterval Math.floor(1000 / fps);} else {yieldInterval 5;} };// 帧结束前执行任务 const performWorkUntilDeadline () {if (scheduledHostCallback ! null) {const currentTime getCurrentTime();// 更新当前帧的结束时间deadline currentTime yieldInterval;const hasTimeRemaining true;try {const hasMoreWork scheduledHostCallback(hasTimeRemaining,currentTime,);// 如果还有调度任务就执行if (!hasMoreWork) {isMessageLoopRunning false;scheduledHostCallback null;} else {// 没有调度任务就通过 postMessage 通知结束port.postMessage(null);}} catch (error) {// ..throw error;}} else {isMessageLoopRunning false;}needsPaint false; };// 通过 MessageChannel 创建消息通道实现任务调度通知 const channel new MessageChannel(); const port channel.port2; channel.port1.onmessage performWorkUntilDeadline;// 通过 postMessage通知 scheduler 已经开始了帧调度 requestHostCallback function(callback) {scheduledHostCallback callback;if (!isMessageLoopRunning) {isMessageLoopRunning true;port.postMessage(null);} };任务中断 前面说到可中断模式下的 workLoop每次遍历执行 performUnitOfWork 前会先判断 shouYield 的值 // packages/react-reconciler/src/ReactFiberWorkLoop.old.jsfunction workLoopConcurrent() {while (workInProgress ! null !shouldYield()) {performUnitOfWork(workInProgress);} }我们看一下 shouYield 的值是如何获取的 // packages\scheduler\src\SchedulerPostTask.js export function unstable_shouldYield() {return getCurrentTime() deadline; }getCurrentTime 获取的是当前的时间戳deadline 上面讲到了是浏览器每一帧结束的时间戳。也就是说 concurrent 模式下react 会将这些非同步任务放到浏览器每一帧空闲时间段去执行若每一帧结束未执行完则中断当前任务待到浏览器下一帧的空闲再继续执行。 总结 总结一下 react render 阶段的设计思想 当发生渲染或者更新操作时react 去创建一系列的任务任务带有优先级然后构建 workInProgress fiber 树链表。遍历任务链表去执行任务。每一帧帧先执行浏览器的渲染等任务如果当前帧还有空闲时间则执行任务直到当前帧的时间用完。如果当前帧已经没有空闲时间就等到下一帧的空闲时间再去执行。如果当前帧没有空闲时间但是当前任务链表有任务到期了或者有立即执行任务那么必须执行的时候就以丢失几帧的代价执行这些任务。执行完的任务都会被从链表中删除。
http://www.w-s-a.com/news/286397/

相关文章:

  • 来宾住房与城乡建设网站天津西青区怎么样
  • 西安网站建设培训班鄂州网页定制
  • 西部数码网站备份自己怎么做网站啊
  • h5网站开发用什么软件制作公司网站建设代理怎么做
  • 网站建设资料准备网上购物app有哪些
  • 沧州做网站优化哪家公司便宜国内百度云网站建设
  • 网站的最近浏览 怎么做龙岩市人才网最新招聘信息
  • 网站建设需要找工信部吗网站开发账务处理
  • 做那种的视频网站个体工商网站备案
  • 推广网官方推广网站中国建设招聘信息网站
  • 医院网站建设需要多少钱网络营销渠道可分为哪几种
  • 怎么取网页视频网站元素计算机专业论文网站开发
  • 上海网站建设备案号怎么恢复wordpress打开页面空白
  • 30个做设计的网站企业设计网站
  • 招生网站开发的背景创意 wordpress
  • 网站备案资料查询小型企业管理系统软件
  • 温州网站建设维护怎么做好网站开发、设计
  • 佛山 做网站公司有哪些网站排名忽然不见了
  • 广告网站建设最专业东莞大朗网站设计
  • 网站做流量的论坛贴吧分销商城系统源码
  • 新手建立网站的步骤网站建设费怎么入分录
  • 哪里建网站性价比高做网站赚取广告费
  • 邢台集团网站建设价格微信怎么做捐钱的网站
  • 做网站费用需要分摊吗装修公司一般多少钱一平方
  • 公司主页的网站格式wordpress自动推送给百度
  • 网站建设辶金手指排名十二wordpress 当数据库
  • 无锡手机网站建设服务苏州展厅设计企业
  • 无锡网站制作需要多少钱北京二次感染最新消息
  • 网站开发视频播放无画面杭州房产信息网官网
  • 网站开发 改进如何创建公众号平台