深入浅出 solid.js 源码 (二十)—— Suspense

280 阅读2分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情

这一节来看 Suspense,在 react 中也有 Suspense 组件,它的作用是等待异步流程,比如加载异步组件,使用 Suspense 就可以这样写:

const AsyncComponent = lazy(() => import('./component'));

<Suspense fallback={<LoadingIndicator />}>
  <AsyncComponent />
</Suspense>

在 solid 中 Suspense 的使用方法和 React 基本相同,它可以处理异步流程数据,前面提到过 solid 中异步数据管理使用的是 createResource 函数,因此这里涉及到的相关渲染就可以用到 Suspense。和上面的例子一样,solid 中也有 lazy 逻辑,这部分也会使用到。

Suspense 位于 render 下面,是一个独立的文件,使用 Suspense 很容易,但是 Suspense 内部需要处理和维护的状态还是比较多的,异步流程控制是前端中很重要的一个环节,这里也是一样。Suspense 最后创建的组件是一个 SuspenseContext.Provider:

return createComponent(SuspenseContext.Provider, {
    value: store,
    get children() {
      return createMemo(() => {
        if (error) throw error;
        ctx = sharedConfig.context!;
        if (flicker) {
          flicker();
          return (flicker = undefined);
        }
        if (ctx && p === undefined) setHydrateContext();
        const rendered = createMemo(() => props.children);
        return createMemo(() => {
          const inFallback = store.inFallback(),
            visibleContent = showContent ? showContent() : true,
            visibleFallback = showFallback ? showFallback() : true;
          dispose && dispose();
          if ((!inFallback || p !== undefined) && visibleContent) {
            store.resolved = true;
            ctx = p = undefined;
            resumeEffects(store.effects);
            return rendered();
          }
          if (!visibleFallback) return;
          return createRoot(disposer => {
            dispose = disposer;
            if (ctx) {
              setHydrateContext({ id: ctx.id + "f", count: 0 });
              ctx = undefined;
            }
            return props.fallback;
          }, owner!);
        });
      });
    }
  });

这里面的关键当然就是 children 了,首先来看第一层的 memo,在这里面会把当前的子内容创建成一个 rendered memo,这部分是实际的渲染内容,具体什么时候渲染呢,这就要交给下一层处理。

下一层依旧是创建了 memo,这里首先会去从 store 获取 fallback 状态,store 被注入到了 SuspenseContext.Provider 中,这里是由其内部修改。如果内容已经渲染好并且不需要进入 fallback,这时恢复 effect 的执行,执行实际的渲染逻辑。如果内容未渲染好,这时渲染 fallback 内容,如果有 fallback 存在就会通过 createRoot 创建一个 fallback 的渲染。

接下来看 store 内部的更新, 这部分是由 increment 和 decrement 两个函数控制的,这部分会在 signal 的执行逻辑中进行调用处理,这部分位于 createResource 中的 loadEnd 中,因此 Suspense 的异步行为是由 createResource 来驱动的,我么可以看 lazy 函数的实现,在其内部也可以看到 createResource 的调用。

除了 Suspense 本身,solid 中还有 SuspenseList API,这部分目前还属于实验性功能,这里不展开内容了。