Skip to content
On this page

异步组件

类型

ts
function defineAsyncComponent(
  source: AsyncComponentLoader | AsyncComponentOptions
): Component

type AsyncComponentLoader = () => Promise<Component>

interface AsyncComponentOptions {
  loader: AsyncComponentLoader
  loadingComponent?: Component
  errorComponent?: Component
  delay?: number
  timeout?: number
  suspensible?: boolean
  onError?: (
    error: Error,
    retry: () => void,
    fail: () => void,
    attempts: number
  ) => any
}

源码

ts
import { ref } from "@vue/reactivity";
import { h } from "./h";
import { Fragment } from "./createVNode";
import { isFunction } from "@vue/shared";
export function defineAsyncComponent(options) {
  if (isFunction(options)) {
    options.loader = options;
  }
  return {
    setup() {
      const { loader } = options;
      let Comp = null;
      const loaded = ref(false);
      const error = ref(false);
      const loading = ref(false);

      if (options.timeout) {
        setTimeout(() => {
          error.value = true; // 时间到了就失败了
        }, options.timeout);
      }
      if (options.delay) {
        setTimeout(() => {
          loading.value = true; // 时间到了就失败了
        }, options.delay);
      }

      function load() {
        return loader() // 1)
          .then((c) => {
            Comp = c.default ? c.default : c;
            loaded.value = true;
          })
          .catch((err) => {
              if (options.onError) {
                  return new Promise((resolve, reject) => { // 2)
                      const retry = () => resolve(load()) // 递归调用
                      const fail = () => reject(err)
                      options.onError(err,retry,fail)
                })
            }
            error.value = true;
          });
      }
      load();
      return () => {
        if (loaded.value) {
          return h(Comp);
        } else if (error.value && options.errorComponent) {
          return h(options.errorComponent);
        } else if (loading.value && options.loadingComponent) {
          return h(options.loadingComponent);
        }
        return h(Fragment, []);
      };
    },
  };
}