Skip to content

异步组件

在大型项目开发中,每个完整的页面都是由无数个小组件构成的。根据轻重缓急的渲染策略,我们可以优先渲染那些主要的组件,而非主要的组件可以延迟渲染,并仅在需要时再加载相关的组件

defineAsyncComponent

为了能够加载异步组件,Vue 提供了 defineAsyncComponent 函数来实现此功能:

js
import { defineAsyncComponent } from 'vue'

// 1. 定义一个异步组件,命名为 AsyncComp
//    defineAsyncComponent(fn) 的入参是一个“返回 Promise 的加载函数”
const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // 2. 从服务器获取组件
    // 3. 如果组件获取成功,则调用 resolve() 并传入获取到的组件
    resolve(/* 获取到的组件 */)
    // 4. 如果获取失败,则调用 reject() 并传入失败的原因
    // reject(reason)
  })
})

// ... 像使用其他一般组件一样使用 `AsyncComp`

从本地加载异步组件

除了可以从服务器加载异步组件,我们还可以结合 defineAsyncComponent + ES 模块动态导入 的方式,从本地加载异步组件:

js
import { defineAsyncComponent } from 'vue'

// 异步加载本地的 SFC 组件
const AsyncComp = defineAsyncComponent(() => import('./components/MyComponent.vue'))

与普通组件一样,异步组件可以使用 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() 也支持提供 loading 提示和 timeout 超时处理。语法格式如下:

js
const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('需要异步加载的本地的 SFC 组件路径'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})

示例代码如下:

vue
<script setup>
import { ref, defineAsyncComponent } from 'vue'
// 展示 loading 提示的组件
import Loading from './components3/Loading.vue'
// 超时之后,需要展示的错误组件
import ErrorPage from './components3/ErrorPage.vue'

// 使用 flag 布尔值,控制是否展示异步组件(这是为了方便调试)
// 刷新网页时不节流,点击按钮前,把网速调成 3G,方便演示异步组件加载慢的情况
const flag = ref(false)

const AdminPage = defineAsyncComponent({
  // 通过 loader 属性指定要一步加载的组件
  loader: () => import('./components3/AdminPageComponent.vue'),
  // 指定 loading 提示组件
  loadingComponent: Loading,
  // 展示 loading 提示组件之前的延迟时间(ms),防止闪烁
  delay: 200,
  // 超时时长:最大等待时长(ms)
  timeout: 1000,
  // 超时之后,显示错误组件
  errorComponent: ErrorPage
})
</script>

<template>
  <button @click="flag = !flag">toggle</button>
  <AdminPage v-if="flag" />
</template>

天不生夫子,万古长如夜