Skip to content
On this page

异步组件

defineAsyncComponent函数是一个高阶组件,他的返回值是一个包装组件。此包装组件会根据状态来决定渲染的内容,加载成功后渲染组件,在未渲染成功时渲染一个占位符节点

基本实现

let asyncComponent = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        render: () => h("div", "hi jiang"),
      });
    }, 1000);
  });
});
export function defineAsyncComponent(loader) {
  let Comp = null;
  return {
    setup() {
      const loaded = ref(false);
      loader().then((c) => {
        Comp = c;
        loaded.value = true;
      });
      return () => {
        return loaded.value ? h(Comp) : h(Fragment, "");
      };
    },
  };
}

异步组件超时处理

let asyncComponent = defineAsyncComponent({
  loader: () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({
          render() {
            return h("div", "hi jiang");
          },
        });
      }, 1000);
    });
  },
  timeout: 2000,
  errorComponent: {
    render: () => h("Text", "超时错误"),
  },
});
export function defineAsyncComponent(options) {
  if (typeof options === "function") {
    options = { loader: options };
  }
  let Comp = null;
  return {
    setup() {
      const { loader } = options;
      const loaded = ref(false);
      const error = ref(false); // 是否超时
      loader()
        .then((c) => {
          Comp = c;
          loaded.value = true;
        })
        .catch((err) => (error.value = err));
      if (options.timeout) {
        setTimeout(() => {
          error.value = true; // 显示错误组件
        }, options.timeout);
      }
      const placeHolder = h(Fragment, "");
      return () => {
        if (loaded.value) {
          return h(Comp);
        } else if (error.value && options.errorComponent) {
          // 超时显示错误组件
          return h(options.errorComponent);
        }
        return placeHolder;
      };
    },
  };
}

异步组件 loading 处理

let asyncComponent = defineAsyncComponent({
  loader: () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({
          render() {
            return h("div", "hi jiang");
          },
        });
      }, 3000);
    });
  },
  timeout: 2000,
  errorComponent: {
    render: () => h("Text", "超时错误"),
  },
  delay: 1000,
  loadingComponent: {
    render: () => h("h2", "loading...."),
  },
});
// loading逻辑
const loading = ref(false);
let loadingTimer = null;
if (options.delay) {
  loadingTimer = setTimeout(() => {
    loading.value = true;
  }, options.delay);
} else {
  loading.value = true;
}
const error = ref(false);
loader()
  .then((c) => {
    Comp = c;
    loaded.value = true;
  })
  .catch((err) => (error.value = err))
  .finally(() => {
    loading.value = false;
    clearTimeout(loadingTimer); // 加载完毕的时候清理定时器
  });
// ...
return () => {
  if (loaded.value) {
    return h(Comp);
  } else if (error.value && options.errorComponent) {
    return h(options.errorComponent);
  } else if (loading.value && options.loadingComponent) {
    // 显示loading组件
    return h(options.loadingComponent);
  }
  return placeHolder;
};

异步组件加载重试处理

let asyncComponent = defineAsyncComponent({
  loader: () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject({
          render() {
            return h("div", "hi jiang");
          },
        });
      }, 3000);
    });
  },
  timeout: 2000,
  errorComponent: {
    render: () => h("Text", "超时错误"),
  },
  delay: 1000,
  loadingComponent: {
    render: () => h("h2", "loading...."),
  },
  onError(retry) {
    console.log("错了");
    retry();
  },
});
function load() {
  return loader().catch((err) => {
    if (options.onError) {
      return new Promise((resolve, reject) => {
        const retry = () => resolve(load());
        const fail = () => reject(err);
        options.onError(retry, fail);
      });
    } else {
      throw err;
    }
  });
}

Released under the MIT License.