如何优雅地使用Vue3的异步组件

简介: 在开发Vue项目时,大多数人都会用到组件。在父组件中,子组件的加载一般是按照先后顺序加载的,子组件加载后才会加载父组件。如果一个页面的子组件很多,由于会先加载子组件,那么父组件可能会出现比较长的白屏等待时间。我们想让`child.vue`组件异步加载应该怎么办呢?Vue3

前言

在开发Vue项目时,大多数人都会用到组件。在父组件中,子组件的加载一般是按照先后顺序加载的,子组件加载后才会加载父组件。举个栗子:

// app.vue
<script setup>
import {onMounted} from 'vue'
import ChildVue from './child.vue'
onMounted(() => {
    console.log('app')
})
</script>
<template>
    <ChildVue />
</template>
// child.vue
<script setup>
import {onMounted} from 'vue'
onMounted(() => {
    console.log('child')
})
</script>

运行结果:

child
app

如果一个页面的子组件很多,由于会先加载子组件,那么父组件可能会出现比较长的白屏等待时间。我们想让child.vue组件异步加载应该怎么办呢?Vue3提供了一个新增的defineAsyncComponent方法来实现异步组件。

异步组件

异步组件这个概念并不是Vue3中才有的,Vue2也有异步组件,不同的是Vue2是通过函数来创建。我们在Vue3中创建异步组件也比较简单,主要是通过defineAsyncComponent方法来创建一个异步组件,然后再需要的时候再使用异步组件即可。defineAsyncComponent主要有两种方式:

使用Promise加载函数的方式创建

defineAsyncComponent接收一个返回Promise的加载函数。我们知道,import默认导入的模块是静态的,如果我们将import用于动态导入模块,那么将放回一个Promise,也就是说我们可以在defineAsyncComponent的加载函数中直接使用import来动态导入一个模块。

若非必要,请不要滥用动态导入

// app.vue
<script setup>
import {onMounted, defineAsyncComponent } from 'vue'
const AsyncChild = defineAsyncComponent(() => import('./child.vue'))
onMounted(() => {
    console.log('app')
})
</script>
<template>
    <AsyncChild />
</template>

我们也可以自己使用new Promise()来创建异步组件:

// app.vue
<script setup>
import {onMounted, defineAsyncComponent } from 'vue'
import Child from './child.vue'
const AsyncChild = defineAsyncComponent(() => (new Promise((resolve, reject) => resolve(Child))))
onMounted(() => {
    console.log('app')
})
</script>
<template>
    <AsyncChild />
</template>

两者的运行结果:

app
child

由此我们可以看到,父组件会先于子组件执行,因此此时的子组件已变成了异步组件。

使用对象的方式创建

采用动态导入的方式创建是简单有效的,不过有时候我们有一些特殊要求,比如说在我们想知道异步组件的加载状态,包括加载中、加载失败等,如果是使用动态导入的方式就无法实现此效果,因此我们需要更高级一点的方式来创建:传入一个特殊对象。

语法说明

const AsyncComp = defineAsyncComponent({
  // 加载函数,需要返回一个Promise,可以使用动态import的方式,也可以自己new Promise()
  loader: () => import('./Foo.vue'),

  // 加载异步组件时使用的组件,该组件会在异步组件加载时显示,如果异步组件加载很快,可能不会出现loading组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

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

语法说明看起来不复杂,我们就来多举几个例子:

首先我们来新建两个加载状态的组件,也挺简单的,就是一句话:

// LoadingComp.vue
<template>
  <div>加载中...</div>
</template>
// ErrorComp
<template>
  <div>加载出错啦...</div>
</template>
  1. 组件加载失败

    我们使用new Promise()的方式来测试,如果是使用动态导入的方式,笔者还没有找到合适的方式测试

    // app.vue
    // app.vue
    <script setup>
    import {ref, onMounted, defineAsyncComponent } from 'vue'
    import LoadingComp from './LoadingComp.vue'
    import ErrorComp from './ErrorComp.vue'
    const AsyncChild = defineAsyncComponent({
        loader: () => (new Promise((resolve, reject) => reject())),
        loadingComponent: LoadingComp,
        delay: 200,
        errorComponent: ErrorComp,
        timeout: 2000
    })
    onMounted(() => {
        console.log('app')
    })
    let isShowAsyncComp = ref(false)
    const loader = () => {
      isShowAsyncComp.value = true
    }
    </script>
    <template>
        <button @click="loader">加载异步组件</button>
        <AsyncChild v-if="isShowAsyncComp" />
    </template>

    运行结果:

    点击加载异步组件按钮,会出现加载出错啦...的报错信息,因为我们在loader加载函数中返回了一个reject()的Promise

    image-20220613103836272.png

  2. 组件加载中

    既然是异步组件,一般都是那些加载需要耗时的组件,那么我们可以通过setTimeout手动来模拟一个耗时加载组件。本次案例还是采用new Promise()的方式:

    // app.vue
    ...
    const AsyncComp = defineAsyncComponent({
      loader: () => (new Promise((resolve, reject) => setTimeout(() => {
        resolve(directiveVue)
      }, 1000))),
      loadingComponent: LoadingComp,
      delay: 200,
      errorComponent: ErrorComp,
      timeout: 2000
    })

    我们假设异步组件需要耗时1000ms,在此之前应该都是显示加载中,运行结果:

    image-20220613104317179.png

配合Suspense使用

Suspense是一个实验性功能,并不保证会成为稳定版,如果非得要使用此功能,建议等版本稳定后使用。因此,我们举的例子仅限于如何使用,至于原理就需要自行去官网查看了:Suspense

// app.vue
<!-- <AsyncComp v-if="isShowAsyncComp" /> -->
  <Suspense v-if="isShowAsyncComp">
    <template #default>
      <AsyncComp />
    </template>
    <template #fallback>
      <p>Suspense 加载中...</p>
    </template>
  </Suspense>

Suspense只能处理加载中状态和加载异步组件本身,并不能进行错误处理。当然你可以选择借助onErrorCaptured()钩子来捕获,或者使用defineAsyncComponent创建异步组件时指定错误组件。

最后再次说明,Suspense只是实验性功能,如果是在实际项目中,并不建议使用Suspense

总结

本文详细介绍了如何创建并使用异步组件,以及处理异步组件的加载状态,简单总结一下:

  • 异步组件采用defineAsyncComponent方法来创建,需要传入一个返回值为Promise的加载函数
  • 创建异步组件的的方式可以有两种方式:Promise、对象
  • 异步组件可配合Suspense使用,请注意这个目前是实验功能,存在诸多不确定性
  • 注意:Vue-Router配置路由时,虽然也可以有类似异步加载机制的方式加载路由组件,但是不应该使用defineAsyncComponent
相关文章
|
8月前
|
JavaScript 前端开发 安全
Vue 3
Vue 3以组合式API、Proxy响应式系统和全面TypeScript支持,重构前端开发范式。性能优化与生态协同并进,兼顾易用性与工程化,引领Web开发迈向高效、可维护的新纪元。(238字)
1114 139
|
缓存 JavaScript PHP
斩获开发者口碑!SnowAdmin:基于 Vue3 的高颜值后台管理系统,3 步极速上手!
SnowAdmin 是一款基于 Vue3/TypeScript/Arco Design 的开源后台管理框架,以“清新优雅、开箱即用”为核心设计理念。提供角色权限精细化管理、多主题与暗黑模式切换、动态路由与页面缓存等功能,支持代码规范自动化校验及丰富组件库。通过模块化设计与前沿技术栈(Vite5/Pinia),显著提升开发效率,适合团队协作与长期维护。项目地址:[GitHub](https://github.com/WANG-Fan0912/SnowAdmin)。
1400 5
|
8月前
|
缓存 JavaScript 算法
Vue 3性能优化
Vue 3 通过 Proxy 和编译优化提升性能,但仍需遵循最佳实践。合理使用 v-if、key、computed,避免深度监听,利用懒加载与虚拟列表,结合打包优化,方可充分发挥其性能优势。(239字)
574 1
|
9月前
|
开发工具 iOS开发 MacOS
基于Vite7.1+Vue3+Pinia3+ArcoDesign网页版webos后台模板
最新版研发vite7+vue3.5+pinia3+arco-design仿macos/windows风格网页版OS系统Vite-Vue3-WebOS。
1042 11
|
8月前
|
JavaScript 安全
vue3使用ts传参教程
Vue 3结合TypeScript实现组件传参,提升类型安全与开发效率。涵盖Props、Emits、v-model双向绑定及useAttrs透传属性,建议明确声明类型,保障代码质量。
659 0
|
10月前
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
1075 1
|
10月前
|
缓存 JavaScript UED
除了循环引用,Vue3还有哪些常见的性能优化技巧?
除了循环引用,Vue3还有哪些常见的性能优化技巧?
529 0
|
11月前
|
JavaScript
vue3循环引用自已实现
当渲染大量数据列表时,使用虚拟列表只渲染可视区域的内容,显著减少 DOM 节点数量。
256 0
|
JavaScript API 容器
Vue 3 中的 nextTick 使用详解与实战案例
Vue 3 中的 nextTick 使用详解与实战案例 在 Vue 3 的日常开发中,我们经常需要在数据变化后等待 DOM 更新完成再执行某些操作。此时,nextTick 就成了一个不可或缺的工具。本文将介绍 nextTick 的基本用法,并通过三个实战案例,展示它在表单验证、弹窗动画、自动聚焦等场景中的实际应用。
1248 17
|
JavaScript 前端开发 API
Vue 2 与 Vue 3 的区别:深度对比与迁移指南
Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架,在过去的几年里,Vue 2 一直是前端开发中的重要工具。而 Vue 3 作为其升级版本,带来了许多显著的改进和新特性。在本文中,我们将深入比较 Vue 2 和 Vue 3 的主要区别,帮助开发者更好地理解这两个版本之间的变化,并提供迁移建议。 1. Vue 3 的新特性概述 Vue 3 引入了许多新特性,使得开发体验更加流畅、灵活。以下是 Vue 3 的一些关键改进: 1.1 Composition API Composition API 是 Vue 3 的核心新特性之一。它改变了 Vue 组件的代码结构,使得逻辑组
2376 0