01. defineAsyncComponent 定义和使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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 }
|
有两个参数 AsyncComponentLoader or AsyncComponentOptions
写了一个 type AsyncComponentLoader = () => Promise 参数的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <div id="app"> <my-async-component></my-async-component> </div>
<script> const MyAsyncComponent = Vue.defineAsyncComponent(() => { return new Promise((resolve) => { setTimeout(() => { resolve({ template: '<div>This is an async component!</div>' }); }, 2000); }); });
const app = Vue.createApp({ components: { MyAsyncComponent } });
app.mount('#app'); </script>
|
既然可以这样写,也可以这样写
1 2 3
| const MyAsyncComponent = Vue.defineAsyncComponent(() => { return import('../a.vue'); });
|
另一种就是写一个 options
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const AsyncComponent = Vue.defineAsyncComponent({ loader: () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ template: '<div>This is an async component!</div>' }); }, 10000); }); }, delay: 200, timeout: 3000, errorComponent: 'div', timeoutComponent: { template: '<div>Component loading timed out.</div>' }, suspensible: true });
|
02. 源码
packages\runtime-core\src\apiAsyncComponent.ts
这个代码看得比较纠结,简单说就是
- 获取参数
- 定义一些局部变量用于跟踪异步加载状态,如 pendingRequest、resolvedComp 和 retries。
- 实现 load 函数,用于执行异步加载过程。它会调用 loader 函数,并处理加载过程中的错误和超时。加载成功后,将得到的组件进行处理,并返回 Promise 对象。
- 使用 defineComponent 定义一个包裹组件 (AsyncComponentWrapper),并在 setup 钩子中处理异步加载的逻辑。
- 在 setup 钩子中,根据加载状态渲染不同的内容,包括已加载的组件、加载中的组件、错误组件等。
- 返回 AsyncComponentWrapper 作为最终的组件选项对象,并使用类型断言 as T 将其转换为泛型参数 T 所指定的类型。
简单说就是这些,我更关注的,这些参数有什么用。
03. delay
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const delayed = ref(!!delay)
if (delay) { setTimeout(() => { delayed.value = false }, delay) }
return () => { if (loaded.value && resolvedComp) { return createInnerComp(resolvedComp, instance) } else if (error.value && errorComponent) { return createVNode(errorComponent, { error: error.value }) } else if (loadingComponent && !delayed.value) { return createVNode(loadingComponent) } }
|
看到这里,知道这个 delay 有什么用了吧,他只和 loadingComponent 相关。
- 判断是否load完成以及解析完成组件
- 判断错误信息和错误组件
- loading组件和是否应该loading
也就是说,delay 是出现 loadingComponent 组件出现的延迟时间
04. timeout
1 2 3 4 5 6 7 8 9 10 11
| if (timeout != null) { setTimeout(() => { if (!loaded.value && !error.value) { const err = new Error( `Async component timed out after ${timeout}ms.` ) onError(err) error.value = err } }, timeout) }
|
这个 timeout稍显简陋,就是一个定时任务,并且无法中断 promise, 也就是 load.
但是,error.value 会触发 render 重新渲染,并且调用,所以会调用 errorComponent
04 suspensible
是否开启了 suspens.
05. defineAsyncComponent 总结
这里基本就知道他是怎么用的了,以及 error,loading 组件加载的原理,以及 setTimeout & delay & suspensible 等参数作用是什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| if ( (__FEATURE_SUSPENSE__ && suspensible && instance.suspense) || (__SSR__ && isInSSRComponentSetup) ) { return load() .then(comp => { return () => createInnerComp(comp, instance) }) .catch(err => { onError(err) return () => errorComponent ? createVNode(errorComponent as ConcreteComponent, { error: err }) : null }) }
|
06. suspense
官方文档 Suspense,其他的简单文档 Vue3 的新功能 — Suspense
是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
这个其实就说的很明确了,刚才说的 defineAsyncComponent 是针对单个组件,Suspense可以对于整个下面的组件都控制。
Suspense 可以等待的异步依赖有两种:
- 带有异步 setup() 钩子的组件。这也包含了使用