react组件的创建与更新实现流程详解

目录
  • React源码执行流程图
  • legacyRenderSubtreeIntoContainer
  • legacyCreateRootFromDOMContainer
  • createLegacyRoot
  • ReactDOMBlockingRoot
  • createRootImpl
  • createContainer
  • createFiberRoot
  • createHostRootFiber
  • createFiber
  • updateContainer
  • 总结

这一章节就来讲讲ReactDOM.render()方法的内部实现与流程吧。

因为初始化的源码文件部分所涵盖的内容很多,包括创建渲染更新渲染Fiber树的创建与diffelement的创建与插入,还包括一些优化算法,所以我就整个的React执行流程画了一个简单的示意图。

React源码执行流程图

从图中我们很清晰的看到ReactDOM.render()之后我们的组件具体干了什么事情,那么我们进入源码文件一探究竟吧。

// packages/react-dom/src/client/ReactDOMLegacy.js
export function render(
  element: React$Element<any>, // 经过babel解析后的element
  container: Container, // 根组件节点: document.getElementById('root')..
  callback: ?Function,// 回调
) {
  // 做合法容器的验证(根组件)
  invariant(
    isValidContainer(container),
    'Target container is not a DOM element.',
  );

  // 开发模式下
  if (__DEV__) {
    const isModernRoot =
      isContainerMarkedAsRoot(container) &&
      container._reactRootContainer === undefined;
    if (isModernRoot) {
      console.error(
        'You are calling ReactDOM.render() on a container that was previously ' +
          'passed to ReactDOM.createRoot(). This is not supported. ' +
          'Did you mean to call root.render(element)?',
      );
    }
  }
  // 返回 legacyRenderSubtreeIntoContainer
  return legacyRenderSubtreeIntoContainer(
    null,
    element,
    container,
    false,
    callback,
  );
}

所以当前render函数仅仅只是做了部分逻辑,阅读React源码,给你一个直观的感受就是他拆分的颗粒度非常的细,很多重复命名的函数,可能是见名知意的变量名只有那么几个常见的组合吧,这也是React作者的用心良苦吧。

追根究底我们还是得看一看legacyRenderSubtreeIntoContainer究竟干了些不为人知的事情呢

legacyRenderSubtreeIntoContainer

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>, // 父级组件
  children: ReactNodeList, // 当前元素
  container: Container, // 容器 eg:getElementById('root')
  forceHydrate: boolean,  callback: ?Function,
) {
  if (__DEV__) {
    topLevelUpdateWarnings(container);
    warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
  }
  // TODO: Without `any` type, Flow says "Property cannot be accessed on any
  // member of intersection type." Whyyyyyy.
  let root: RootType = (container._reactRootContainer: any);
  let fiberRoot;
  // 如果有根组件,表示不是初始化渲染,则走下面的批量更新
  // 没有根组件,那么就要去创建根组件了
  if (!root) {
    // 初始化挂载
    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);
      };
    }
    // 批量更新
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}
  • 有根节点的情况下,我们判定为非首次渲染状态,执行updateContainer
  • 没有根节点的情况下,我们判定为首次渲染,接着去创建根节点,执行legacyCreateRootFromDOMContainer,拿到了root之后,我们会去触发执行updateContainer

legacyCreateRootFromDOMContainer

function legacyCreateRootFromDOMContainer(
  container: Container, // 容器
  forceHydrate: boolean, // value:false
): RootType {
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  // First clear any existing content.
  if (!shouldHydrate) {
    let warned = false;
    let rootSibling;
    while ((rootSibling = container.lastChild)) {
      if (__DEV__) {
        if (
          !warned &&
          rootSibling.nodeType === ELEMENT_NODE &&
          (rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME)
        ) {
          warned = true;
          console.error(
            'render(): Target node has markup rendered by React, but there ' +
              'are unrelated nodes as well. This is most commonly caused by ' +
              'white-space inserted around server-rendered markup.',
          );
        }
      }
      container.removeChild(rootSibling);
    }
  }
  if (__DEV__) {
    if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
      warnedAboutHydrateAPI = true;
      console.warn(
        'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
          'will stop working in React v18. Replace the ReactDOM.render() call ' +
          'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
      );
    }
  }
  // 关注createLegacyRoot
  return createLegacyRoot(
    container,
    shouldHydrate
      ? {
          hydrate: true,
        }
      : undefined,
  );
}

createLegacyRoot

export function createLegacyRoot(
  container: Container, // 容器
  options?: RootOptions,
): RootType {
  //关注ReactDOMBlockingRoot
  return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}

ReactDOMBlockingRoot

function ReactDOMBlockingRoot(
  container: Container, // 容器
  tag: RootTag, // LegacyRoot = 0;BlockingRoot = 1;ConcurrentRoot = 2;
  options: void | RootOptions,
) {
  this._internalRoot = createRootImpl(container, tag, options);
}
  • 我们在这里看到this._internalRoot出来了,因为在先前这个值会给到fiberRoot,所以我们再去看一看这个_internalRoot是怎么创建出来的
  • 相关参考视频讲解:进入学习

createRootImpl

function createRootImpl(
  container: Container,  tag: RootTag,  options: void | RootOptions,
) {
  // Tag is either LegacyRoot or Concurrent Root
  const hydrate = options != null && options.hydrate === true;
  const hydrationCallbacks =
    (options != null && options.hydrationOptions) || null;
  const mutableSources =
    (options != null &&
      options.hydrationOptions != null &&
      options.hydrationOptions.mutableSources) ||
    null;
  // 关注createContainer
  const root = createContainer(container, tag, hydrate, hydrationCallbacks);
  markContainerAsRoot(root.current, container);
  const containerNodeType = container.nodeType;
  if (enableEagerRootListeners) {
    const rootContainerElement =
      container.nodeType === COMMENT_NODE ? container.parentNode : container;
    listenToAllSupportedEvents(rootContainerElement);
  } else {
    if (hydrate && tag !== LegacyRoot) {
      const doc =
        containerNodeType === DOCUMENT_NODE
          ? container
          : container.ownerDocument;
      // We need to cast this because Flow doesn't work
      // with the hoisted containerNodeType. If we inline
      // it, then Flow doesn't complain. We intentionally
      // hoist it to reduce code-size.
      eagerlyTrapReplayableEvents(container, ((doc: any): Document));
    } else if (
      containerNodeType !== DOCUMENT_FRAGMENT_NODE &&
      containerNodeType !== DOCUMENT_NODE
    ) {
      ensureListeningTo(container, 'onMouseEnter', null);
    }
  }
  if (mutableSources) {
    for (let i = 0; i < mutableSources.length; i++) {
      const mutableSource = mutableSources[i];
      registerMutableSourceForHydration(root, mutableSource);
    }
  }
  // 关注root
  return root;
}

见名知意关注createContainer为创建容器,看其源码

createContainer

// packages/react-reconciler/src/ReactFiberReconciler.old.js
export function createContainer(
  containerInfo: Container, // 容器
  tag: RootTag, // LegacyRoot = 0;BlockingRoot = 1;ConcurrentRoot = 2;
  hydrate: boolean,  hydrationCallbacks: null | SuspenseHydrationCallbacks,
): OpaqueRoot {
  // 关注createFiberRoot
  return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
}

createFiberRoot

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;
  }
  // 关注createHostRootFiber
  const uninitializedFiber = createHostRootFiber(tag);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  // 初始化更新队列
  initializeUpdateQueue(uninitializedFiber);
  return root;
}

关注 root.currentuninitializedFiber.stateNode这两个玩意儿,后面有大作用,我们还是看看createHostRootFiber

createHostRootFiber

export function createHostRootFiber(tag: RootTag): Fiber {
  let mode;
  if (tag === ConcurrentRoot) {
    mode = ConcurrentMode | BlockingMode | StrictMode;
  } else if (tag === BlockingRoot) {
    mode = BlockingMode | StrictMode;
  } else {
    mode = NoMode;
  }
  if (enableProfilerTimer && isDevToolsPresent) {
    // Always collect profile timings when DevTools are present.
    // This enables DevTools to start capturing timing at any point–
    // Without some nodes in the tree having empty base times.
    mode |= ProfileMode;
  }
  return createFiber(HostRoot, null, null, mode);
}

一眼望去这里便是对tag的处理,到了后面便是去创建fiber节点

createFiber

const createFiber = function(
  tag: WorkTag,  pendingProps: mixed,  key: null | string,  mode: TypeOfMode,
): Fiber {
  // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
  return new FiberNode(tag, pendingProps, key, mode);
};

那么主角出来了,就是我们的FiberNode,这里才走完初始化的创建流程,

所以大致的流程就是上面的图里画的那样子,创建流程我们就告一段落,那我们再去看看更新的流程是怎么玩的。

我们知道除了ReactDOM.render()会触发更新流程之外,我们还有setState强制更新hooks里面的setXxxx等等手段可以触发更新,所谓setState那么不正好是我们Component原型上挂的方法嘛。我们回顾一下Component,那些更新都是调用了updater触发器上的方法,那么我们去看一下这个东西。

const classComponentUpdater = {
  isMounted,
  // setState
  enqueueSetState(inst, payload, callback) {
    const fiber = getInstance(inst);
    const eventTime = requestEventTime(); // 获取更新触发的时间
    const lane = requestUpdateLane(fiber); // 获取任务优先级
    //根据更新触发时间 + 更新优先级来创建更新任务对象
    const update = createUpdate(eventTime, lane); // 创建更新任务对象
    // const update: Update<*> = {
    //   eventTime, // 更新时间
    //   lane, // 优先级
    //   tag: UpdateState, // 更新类型:0更新,1替换。,2强制替换,3捕获型更新
    //   payload: null,// 需要更新的内容
    //   callback: null, // 更新完后的回调
    //   next: null, // 指向下一个更新
    // };
    // 把内容填上
    update.payload = payload;
    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        // 开发环境下腰给个警告
        warnOnInvalidCallback(callback, 'setState');
      }
      // 如果有回调,那么加上回调
      update.callback = callback;
    }
    // const update: Update<*> = {
      //   eventTime, // 更新时间 you
      //   lane, // 优先级 you
      //   tag: UpdateState, // 更新类型:0更新,1替换。,2强制替换,3捕获型更新
      //   payload: null,// 需要更新的内容 you
      //   callback: null, // 更新完后的回调 you
      //   next: null, // 指向下一个更新
      // };
    enqueueUpdate(fiber, update);// 推入更新队列
    scheduleUpdateOnFiber(fiber, lane, eventTime);// 调度
    if (__DEV__) {
      if (enableDebugTracing) {
        if (fiber.mode & DebugTracingMode) {
          const name = getComponentName(fiber.type) || 'Unknown';
          logStateUpdateScheduled(name, lane, payload);
        }
      }
    }
    if (enableSchedulingProfiler) {
      markStateUpdateScheduled(fiber, lane);
    }
  },
  // replaceState
  enqueueReplaceState(inst, payload, callback) {
    const fiber = getInstance(inst);
    const eventTime = requestEventTime();
    const lane = requestUpdateLane(fiber);
    const update = createUpdate(eventTime, lane);
    update.tag = ReplaceState;
    update.payload = payload;
    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'replaceState');
      }
      update.callback = callback;
    }
    enqueueUpdate(fiber, update);
    scheduleUpdateOnFiber(fiber, lane, eventTime);
    if (__DEV__) {
      if (enableDebugTracing) {
        if (fiber.mode & DebugTracingMode) {
          const name = getComponentName(fiber.type) || 'Unknown';
          logStateUpdateScheduled(name, lane, payload);
        }
      }
    }
    if (enableSchedulingProfiler) {
      markStateUpdateScheduled(fiber, lane);
    }
  },
  // forceUpdate
  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 (__DEV__) {
      if (enableDebugTracing) {
        if (fiber.mode & DebugTracingMode) {
          const name = getComponentName(fiber.type) || 'Unknown';
          logForceUpdateScheduled(name, lane);
        }
      }
    }
    if (enableSchedulingProfiler) {
      markForceUpdateScheduled(fiber, lane);
    }
  },
};

updateContainer

export function updateContainer(
  element: ReactNodeList,  container: OpaqueRoot,  parentComponent: ?React$Component<any, any>,  callback: ?Function,
): Lane {
  if (__DEV__) {
    onScheduleRoot(container, element);
  }
  const current = container.current;
  const eventTime = requestEventTime();
  if (__DEV__) {
    // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
    if ('undefined' !== typeof jest) {
      warnIfUnmockedScheduler(current);
      warnIfNotScopedWithMatchingAct(current);
    }
  }
  const lane = requestUpdateLane(current);
  if (enableSchedulingProfiler) {
    markRenderScheduled(lane);
  }
  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }
  if (__DEV__) {
    if (
      ReactCurrentFiberIsRendering &&
      ReactCurrentFiberCurrent !== null &&
      !didWarnAboutNestedUpdates
    ) {
      didWarnAboutNestedUpdates = true;
      console.error(
        'Render methods should be a pure function of props and state; ' +
          'triggering nested component updates from render is not allowed. ' +
          'If necessary, trigger nested updates in componentDidUpdate.\n\n' +
          'Check the render method of %s.',
        getComponentName(ReactCurrentFiberCurrent.type) || 'Unknown',
      );
    }
  }
  const update = createUpdate(eventTime, lane);// 创建更新任务
  // Caution: React DevTools currently depends on this property
  // being called "element".
  update.payload = {element};
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    if (__DEV__) {
      if (typeof callback !== 'function') {
        console.error(
          'render(...): Expected the last optional `callback` argument to be a ' +
            'function. Instead received: %s.',
          callback,
        );
      }
    }
    update.callback = callback;
  }
  enqueueUpdate(current, update); // 推入更新队列
  scheduleUpdateOnFiber(current, lane, eventTime); // 进行调度
  return lane;
}

我们看到了enqueueSetStateenqueueReplaceStateenqueueForceUpdate还是初始化时候走的updateContainer都是走了几乎一样的逻辑:requestEventTime => requestUpdateLane => createUpdate => enqueueUpdate => scheduleUpdateOnFiber

总结

本章从ReactDOM.render()开始讲解了,初始化的时候,根节点的创建与更新流程,以及在类组件原型上挂载的一些更新的方法,但是为什么这一章不直接把他更新流程讲完呢?因为下一章要讲一下fiberNode这个东西,简而言之他只是一个架构概念,并不是React独有的,但是现在很有必要一起来看一看这个,那么下一章我们来一起揭开FiberNode的神秘面纱吧

到此这篇关于react组件的创建与更新实现流程详解的文章就介绍到这了,更多相关react组件的创建与更新内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • react 组件实现无缝轮播示例详解

    目录 正文 无缝轮播 实现思路 构思使用时代码结构 Carousel组件 CarouselItem组件 完善组件 完成小圆点 正文 需求是做一个无缝轮播图,我说这不是有很多现成的轮子吗?后来了解到他有一个特殊的需求,他要求小圆点需要在轮播图外面,因为现在大部分插件都是将小圆点写在轮播图内部的,这对于不了解插件内部结构的小伙伴确实不知道如何修改. 很久没有写插件的我准备写一个插件(react) 无缝轮播 无缝轮播从最后一张到第一张的过程中不会原路返回,它就像轮子似的,从结束到开始是无缝连接的,非常

  • React组件如何优雅地处理异步数据详解

    目录 前言 API介绍 Suspense Error Boundaries 完整方案 处理异步请求的子组件 外层组件 总结 前言 我们在编写React应用的时候,常常需要在组件里面异步获取数据.因为异步请求是需要一定时间才能结束的,通常我们为了更好的用户体验会在请求还没有结束前给用户展示一个loading的状态,然后如果发生了错误还要在页面上面展示错误的原因,只有当请求结束并且没有错误的情况下,我们才渲染出最终的数据.这个需求十分常见,如果你的代码封装得比较好的话,你的处理逻辑大概是这样的: c

  • React组件的应用介绍

    目录 1. 介绍 2. 组件的创建方式 2.1 函数创建组件 2.2 类组件 3. 父子组件传值 3.1 函数组件 3.2 类组件 1. 介绍 组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思.从概念上类似于 JavaScript 类或函数.它接受任意的形参( props ),并返回用于描述页面展示内容的 React元素( jsx ). 定义好的组件一定要有返回值. react中定义组件的2种方式 函数组件(无状态组件/UI组件) 类组件(状态组件/容器组件) 2. 组件

  • React组件化的一些额外知识点补充

    目录 React的额外补充 Portals的使用 Fragment的使用 严格模式StrictMode 总结 React的额外补充 Portals的使用 某些情况下,我们希望渲染的内容独立于父组件,甚至是独立于当前挂载到的DOM元素中(默认都是挂载到id为root的DOM 元素上的). Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案: 第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment; 第二个参数(conta

  • React HOC高阶组件深入讲解

    目录 1. 概念 2. 属性代理 2.1 代理props 2.2 条件渲染 2.3 添加状态 3. 反向继承 3.1 拦截渲染 3.2 劫持生命周期 3.3 操作state 3.4 修改react树 3.5 记录渲染性能 4. 使用装饰器 4.1 安装和配置 4.2 使用 5.总结 1. 概念 高阶组件和高阶函数的类似,使用函数接收一个组件,并返回一个组件. function withList(WrapComponent) { return class extends Component { r

  • react组件的创建与更新实现流程详解

    目录 React源码执行流程图 legacyRenderSubtreeIntoContainer legacyCreateRootFromDOMContainer createLegacyRoot ReactDOMBlockingRoot createRootImpl createContainer createFiberRoot createHostRootFiber createFiber updateContainer 总结 这一章节就来讲讲ReactDOM.render()方法的内部实现

  • react电商商品列表的实现流程详解

    目录 整体页面效果 项目技术点 拦截器的配置 主页面 添加商品 分页与搜索 修改商品 删除商品 完整代码 整体页面效果 项目技术点 antd组件库,@ant-design/icons antd的图标库 axios 接口请求,拦截器配置 node-sass sass-loader css样式的一个嵌套 react-router-dom react路由使用 react-redux redux hooks:大多数我们用的是函数组件,函数组件没有state属性,所以我们使用hooks来初始化数据,并且函

  • React Native采用Hermes热更新打包方案详解

    目录 1, 背景 2,热更新传统方案 3,使用Hermes打包 1, 背景 如果我们打开RN的Android源码,在build.gradle中回看到这样一段代码. if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; debugImplementation files(hermesPath + "hermes-debug.aar") releaseImple

  • Django项目创建及管理实现流程详解

    1.主题 这部分教程主要介绍如何通过Pycharm创建.管理.运行一个Django工程.对于Django模块的相关知识大家可以参考Python社区. 2.准备环境 Django版本为2.0或更高Pycharm版本2017Python3.6解释器 3.创建一个新工程 实际上所有工程的创建都可以通过单击Welcome screen界面上的Create New Project按钮来实现. 如果你已经打开了一个工程,可以通过菜单栏File → New Project...来创建一个新的工程.接下来在Cr

  • React实现卡片拖拽效果流程详解

    前提摘要: 学习宋一玮 React 新版本 + 函数组件 &Hooks 优先 开篇就是函数组件+Hooks 实现的效果如下: 学到第11篇了 照葫芦画瓢,不过老师在讲解的过程中没有考虑拖拽目标项边界问题,我稍微处理了下这样就实现拖拽流畅了 下面就是主要的代码了,实现拖拽(src/App.js): 核心在于标记当前项,来源项,目标项,并且在拖拽完成时对数据处理,更新每一组数据(useState): /** @jsxImportSource @emotion/react */ // 上面代码是使用e

  • React的生命周期函数初始挂载更新移除详解

    目录 概述 constructor 初始 挂载 更新 移除 概述 在React中,生命周期函数指的是组件在某一个时刻会自动执行的函数 constructor 在类或组件创建的时候被自动执行,我们可以说它是生命周期函数,但它并不是React所特有的,所有的Es6对象都有这个函数,所以并不能说它是React的生命周期函数 初始 当数据发生变化时,render函数会被自动执行,符合我们对React生命周期函数的定义,所以它是React的生命周期函数,但在初始阶段,并不会有任何的React生命周期函数被

  • OpenDataV低代码平台新增组件流程详解

    目录 正文 创建组件目录和文件 初始化组件文件 组件配置项 样式配置 属性配置 属性使用 总结 正文 OpenDataV计划采用子库的方式添加子组件,即每一个组件都当做一个子库,子库有自己的依赖,而项目本身的依赖只针对框架,因此每一个组件我们都当做一个子库来开发.下面我带着大家一步步详细的开发一个数字展示组件. 创建组件目录和文件 进入组件库目录下 所有的可拖拽组件都存放在src/resource/components目录下 cd src/resource/components 根据组件名称创建

  • react装饰器与高阶组件及简单样式修改的操作详解

    使用装饰器调用 装饰器 用来装饰类的,可以增强类,在不修改类的内部的源码的同时,增强它的能力(属性或方法) 装饰器使用@函数名写法,对类进行装饰,目前在js中还是提案,使用需要配置相关兼容代码库. react脚手架创建的项目默认是不支持装饰器,需要手动安装相关模块和添加配置文件 安装相关模块 yarn add -D customize-cra react-app-rewired  @babel/plugin-proposal-decorators 修改package.json文件中scripts

  • react时间分片实现流程详解

    目录 什么是时间分片 为什么需要时间分片 实现分片开启 - 固定 为什么用performance.now()而不用Date.now() 实现分片中断.重启 - 连续 分片中断 分片重启 实现延迟执行 - 有间隔 为什么选择宏任务实现异步执行 时间分片异步执行方案的演进 时间分片简单实现 总结 我们常说的调度,可以分为两大模块,时间分片和优先级调度 时间分片的异步渲染是优先级调度实现的前提 优先级调度在异步渲染的基础上引入优先级机制控制任务的打断.替换. 本节将从时间分片的实现剖析react的异步

随机推荐