异步组件 基本用法 在大型应用中,我们可能需要将应用分割成更小的块,并且只在需要时从服务器加载组件。为此,Vue提供了一个 defineAsyncComponent 函数
jsimport { 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
jsimport { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)生成的 AsyncComp 是一个包装组件,它仅在页面实际渲染时调用加载函数。此外,它还会将任何props和slots传递给内部组件,因此你可以使用异步包装器无缝替换原始组件,同时实现懒加载。
与普通组件一样,异步组件可以通过 app.component() 使用 全局注册
jsapp.component('MyComponent', defineAsyncComponent(() =>
import('./components/MyComponent.vue')
))在 本地注册组件 时也可以使用 defineAsyncComponent
vue
import { defineAsyncComponent } from 'vue'
export default {
components: {
AdminPage: defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)
}
}
它们也可以直接在其父组件内部定义
vue
import { defineAsyncComponent } from 'vue'
const AdminPage = defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)
加载和错误状态 异步操作不可避免地涉及加载和错误状态 - defineAsyncComponent() 通过高级选项支持处理这些状态
jsconst 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 激活。
jsimport { defineAsyncComponent, hydrateOnIdle } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnIdle(/* optionally pass a max timeout */)
})可见时激活 通过 IntersectionObserver 使元素可见时激活。
jsimport { defineAsyncComponent, hydrateOnVisible } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnVisible()
})可以可选地传递一个选项对象给观察者。
jshydrateOnVisible({ rootMargin: '100px' })媒体查询时激活 当指定的媒体查询匹配时激活。
jsimport { defineAsyncComponent, hydrateOnMediaQuery } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnMediaQuery('(max-width:500px)')
})交互时激活 当在组件元素上触发指定的事件时激活。激活的 hydration 事件将在 hydration 完成后重放。
jsimport { defineAsyncComponent, hydrateOnInteraction } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnInteraction('click')
})也可以是多个事件类型的列表。
jshydrateOnInteraction(['wheel', 'mouseover'])自定义策略 tsimport { 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 一起使用 异步组件可以与内置的