跳转到内容

异步组件

基本用法

在大型应用中,我们可能需要将应用分割成更小的块,并且只在需要时从服务器加载组件。为此,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')
))

本地注册组件 时也可以使用 defineAsyncComponent

vue
<script>
import { defineAsyncComponent } from 'vue'

export default {
  components: {
    AdminPage: defineAsyncComponent(() =>
      import('./components/AdminPageComponent.vue')
    )
  }
}
</script>

<template>
  <AdminPage />
</template>

它们也可以直接在其父组件内部定义

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> 和异步组件之间交互的文档,请参阅 专门的章节

异步组件已加载