外观
异步组件
基本用法
在大型应用中,我们可能需要将应用分割成更小的块,并且只在需要时从服务器加载组件。为此,Vue提供了一个 defineAsyncComponent
函数
js
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...load component from server
resolve(/* loaded component */)
})
})
// ... use `AsyncComp` like a normal component
如你所见,defineAsyncComponent
接受一个返回Promise的加载函数。当从服务器检索到组件定义时,应调用Promise的resolve
回调。你也可以调用 reject(reason)
来指示加载失败。
ES模块动态导入 也返回一个Promise,所以我们通常将它与 defineAsyncComponent
结合使用。Vite和webpack等打包器也支持这种语法(并将用作包分割点),因此我们可以用它来导入Vue SFC
js
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
生成的 AsyncComp
是一个包装组件,它仅在页面实际渲染时调用加载函数。此外,它还会将任何props和slots传递给内部组件,因此你可以使用异步包装器无缝替换原始组件,同时实现懒加载。
与普通组件一样,异步组件可以通过 app.component()
使用 全局注册
js
app.component('MyComponent', defineAsyncComponent(() =>
import('./components/MyComponent.vue')
))
它们也可以直接在其父组件内部定义
vue
<script setup>
import { defineAsyncComponent } from 'vue'
const AdminPage = defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)
</script>
<template>
<AdminPage />
</template>
加载和错误状态
异步操作不可避免地涉及加载和错误状态 - defineAsyncComponent()
通过高级选项支持处理这些状态
js
const AsyncComp = defineAsyncComponent({
// the loader function
loader: () => import('./Foo.vue'),
// A component to use while the async component is loading
loadingComponent: LoadingComponent,
// Delay before showing the loading component. Default: 200ms.
delay: 200,
// A component to use if the load fails
errorComponent: ErrorComponent,
// The error component will be displayed if a timeout is
// provided and exceeded. Default: Infinity.
timeout: 3000
})
如果提供了加载组件,它将在内部组件加载时首先显示。在加载组件显示之前有一个默认的200ms延迟 - 这是因为在快速网络上,加载状态可能替换得太快,最终看起来像闪烁。
如果提供了错误组件,当加载函数返回的Promise被拒绝时,它将显示。你也可以指定一个超时时间,当请求耗时过长时显示错误组件。
延迟水合
本节仅适用于使用 服务器端渲染 的情况。
在Vue 3.5+中,异步组件可以通过提供水合策略来控制它们何时水合。
Vue提供了一些内置的水合策略。这些内置策略需要单独导入,以便在不使用时进行摇树优化。
设计故意保持低级以增加灵活性。未来可以在核心或高级解决方案(例如 Nuxt)之上构建编译器语法糖。
空闲时激活
通过 requestIdleCallback
激活。
js
import { defineAsyncComponent, hydrateOnIdle } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnIdle(/* optionally pass a max timeout */)
})
可见时激活
通过 IntersectionObserver
使元素可见时激活。
js
import { defineAsyncComponent, hydrateOnVisible } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnVisible()
})
可以可选地传递一个选项对象给观察者。
js
hydrateOnVisible({ rootMargin: '100px' })
媒体查询时激活
当指定的媒体查询匹配时激活。
js
import { defineAsyncComponent, hydrateOnMediaQuery } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnMediaQuery('(max-width:500px)')
})
交互时激活
当在组件元素上触发指定的事件时激活。激活的 hydration 事件将在 hydration 完成后重放。
js
import { defineAsyncComponent, hydrateOnInteraction } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnInteraction('click')
})
也可以是多个事件类型的列表。
js
hydrateOnInteraction(['wheel', 'mouseover'])
自定义策略
ts
import { defineAsyncComponent, type HydrationStrategy } from 'vue'
const myStrategy: HydrationStrategy = (hydrate, forEachElement) => {
// forEachElement is a helper to iterate through all the root elements
// in the component's non-hydrated DOM, since the root can be a fragment
// instead of a single element
forEachElement(el => {
// ...
})
// call `hydrate` when ready
hydrate()
return () => {
// return a teardown function if needed
}
}
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: myStrategy
})
与 Suspense 一起使用
异步组件可以与内置的 <Suspense>
组件一起使用。有关 <Suspense>
和异步组件之间交互的文档,请参阅 专门的章节。