Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React.Suspense 的功能和原理 #39

Open
nmsn opened this issue Apr 12, 2023 · 1 comment
Open

React.Suspense 的功能和原理 #39

nmsn opened this issue Apr 12, 2023 · 1 comment
Labels

Comments

@nmsn
Copy link
Contributor

nmsn commented Apr 12, 2023

如题

@nmsn nmsn added the React label Apr 12, 2023
@nmsn
Copy link
Contributor Author

nmsn commented Apr 12, 2023

https://segmentfault.com/a/1190000042711084

作用

 const Lazy = React.lazy(() => import("./LazyComponent"))
 <Suspense fallback={"loading"}>
        <Lazy/> // lazy 包装的组件
 </Suspense>

为异步加载的组件提供容器,并在未加载完成时展示 fallback 展示的组件

原理

总体流程

  1. beginWork 遍历到 Suspense 组件时,子组件 children 是一个未加载完成的组件
  2. 这时 children 组件会抛出一个异常,异常抛出一个 promise
  3. react 捕获到异常后,判断是 promise,会添加一个 then 方法,一个回调函数,回调函数的作用是触发 Suspense 组件的更新
  4. 将下一个需要遍历的元素重新设置为 Suspense
  5. 第二次遍历 Suspense 时,将 children fallback 都生成,但是会直接返回 fallback(children 必定没有加载完成)
  6. 当 children 加载完成后,会触发 then,更新 Susupense,加载 children 组件

关键节点

  do {
    try {
      workLoopSync();
      break;
    } catch (thrownValue) {
      handleError(root, thrownValue);
    }
  } while (true);

错误捕获代码,是由 workLoopSync 阶段的兜底函数 handleError 来处理的

handleError 中

throwException(
    root,
    erroredWork.return,
    erroredWork,
    thrownValue,
    workInProgressRootRenderLanes,
);
completeUnitOfWork(erroredWork);

---

// 首先判断是否是为 promise
if (
    value !== null &&
    typeof value === 'object' &&
    typeof value.then === 'function'
  ) {
    const wakeable: Wakeable = (value: any);
    resetSuspendedComponent(sourceFiber, rootRenderLanes);
    // 获取到 Suspens 父组件
    const suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
    if (suspenseBoundary !== null) {
      suspenseBoundary.flags &= ~ForceClientRender;
      // 给 Suspens 父组件 打上一些标记,让 Suspens 父组件知道已经有异常抛出,需要渲染 fallback
      markSuspenseBoundaryShouldCapture(
        suspenseBoundary,
        returnFiber,
        sourceFiber,
        root,
        rootRenderLanes,
      );
      // We only attach ping listeners in concurrent mode. Legacy Suspense always
      // commits fallbacks synchronously, so there are no pings.
      if (suspenseBoundary.mode & ConcurrentMode) {
        attachPingListener(root, wakeable, rootRenderLanes);
      }
      // 将抛出的 promise 放入Suspense 父组件的 updateQueue 中,后续会遍历这个 queue 进行回调绑定
      attachRetryListener(suspenseBoundary, root, wakeable, rootRenderLanes);
      return;
    } 
  }

捕获错误后会给 Suspense 的父组件打上标签,将 promise 放入更新队列

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant