Vue3 异步组件(懒加载组件)

Vue3 异步组件(懒加载组件)

  • [1. 基本用法](#1. 基本用法)
    • [1.1 基本语法(defineAsyncComponent )](#1.1 基本语法(defineAsyncComponent ))
    • [1.2 使用示例](#1.2 使用示例)
      • [1.2.1 非异步组件(正常 import,会导致初始加载时间变长)](#1.2.1 非异步组件(正常 import,会导致初始加载时间变长))
      • [1.2.2 异步组件(使用 defineAsyncComponent 异步加载组件,减少初始加载时间 )](#1.2.2 异步组件(使用 defineAsyncComponent 异步加载组件,减少初始加载时间 ))
  • [2. 使用细节](#2. 使用细节)
    • [2.1 注册方式](#2.1 注册方式)
      • [2.1.1 全局注册](#2.1.1 全局注册)
      • [2.1.2 局部注册(通常配合 is 和 v-if 进行使用)](#2.1.2 局部注册(通常配合 is 和 v-if 进行使用))
    • [2.2 加载与错误状态(delay 和 timeout 无效)](#2.2 加载与错误状态(delay 和 timeout 无效))

1. 基本用法

异步组件,就是需要时才异步加载的组件

1.1 基本语法(defineAsyncComponent )

在 Vue3 中,我们可以通过 defineAsyncComponent 函数来实现异步加载组件:

javascript 复制代码
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */)
  })
})
// ... 像使用其他一般组件一样使用 `AsyncComp`

ES 模块动态导入也会返回一个 Promise,所以多数情况下我们会将它和 defineAsyncComponent 搭配使用:

javascript 复制代码
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

最后得到的 AsyncComp 是一个外层包装过的组件,仅在页面需要它渲染时才会调用加载内部实际组件的函数。它会将接收到的 props 和插槽传给内部组件,所以你可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载。

1.2 使用示例

1.2.1 非异步组件(正常 import,会导致初始加载时间变长)

在我们的项目中,通常我们会使用 import 直接导入需要的组件。

父组件 App.vue:

javascript 复制代码
<template>
  <div id="app">
    <button @click="currentComponent = A">访问A组件</button>
    <button @click="currentComponent = B">访问B组件</button>
    <component :is="currentComponent" v-if="currentComponent"></component>
  </div>
</template>

<script setup>
import { shallowRef } from 'vue'
import A from '@/components/A.vue'
import B from '@/components/B.vue'
const currentComponent = shallowRef(null)
</script>

<style lang="scss" scoped>
button {
  margin-right: 10px;
}
</style>

这样在应用启动时就会立即加载所有被导入的组件,从而导致初始的加载时间变长,特别是当组件特别多的时候。

子组件 A.vue:

javascript 复制代码
<template>
  <div>这是A组件</div>
</template>

<script setup>

</script>

<style lang="scss" scoped>

</style>

子组件 B.vue:

javascript 复制代码
<template>
  <div>这是B组件</div>
</template>

<script setup>

</script>

<style lang="scss" scoped>

</style>

1.2.2 异步组件(使用 defineAsyncComponent 异步加载组件,减少初始加载时间 )

此时我们就可以使用异步组件来进行优化

javascript 复制代码
<template>
  <div id="app">
    <button @click="loadComponent('A')">访问A组件</button>
    <button @click="loadComponent('B')">访问B组件</button>
    <component :is="currentComponent" v-if="currentComponent"></component>
  </div>
</template>

<script setup>
import { shallowRef, defineAsyncComponent  } from 'vue'
const currentComponent = shallowRef(null)

const loadComponent = (name) => {
  currentComponent.value = defineAsyncComponent(() => import(`./components/${name}.vue`))
}
</script>

<style lang="scss" scoped>
button {
  margin-right: 10px;
}
</style>

经过改动后,现在只有点击了按钮,才会 import 对应的组件,从而减少了初始加载的时间,实现了懒加载的特性。

2. 使用细节

2.1 注册方式

2.1.1 全局注册

与普通组件一样,异步组件可以使用 app.component() 全局注册。示例代码:

javascript 复制代码
app.component('MyComponent', defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
))

完整示例如下:
main.js

javascript 复制代码
import { createApp, defineAsyncComponent} from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.component('A', defineAsyncComponent(() =>
  import('@/components/A.vue')
))

app.component('B', defineAsyncComponent(() =>
  import('@/components/B.vue')
))


app.use(router)

app.mount('#app')

App.vue

javascript 复制代码
<template>
  <div id="app">
    <button @click="loadComponent('A')">访问A组件</button>
    <button @click="loadComponent('B')">访问B组件</button>
    <component :is="currentComponent" v-if="currentComponent"></component>
  </div>
</template>

<script setup>
import { shallowRef  } from 'vue'
const currentComponent = shallowRef(null)

const loadComponent = (name) => {
  currentComponent.value = name
}
</script>

<style lang="scss" scoped>
button {
  margin-right: 10px;
}
</style>

2.1.2 局部注册(通常配合 is 和 v-if 进行使用)

也可以直接在父组件中直接定义它们:

javascript 复制代码
<template>
  <div id="app">
    <C />
  </div>
</template>

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

const C = defineAsyncComponent(() =>
  import('@/components/C.vue')
)
</script>

<style lang="scss" scoped>
button {
  margin-right: 10px;
}
</style>

不过,我们通常不会直接在组件中使用 <C /> 进行使用,因为异步组件的目的,就是为了需要时加载,所以通常会配合
<component :is="currentComponent" v-if="currentComponent">

这样的语句进行使用,可参考 1.2.2 异步组件 的使用示例。

2.2 加载与错误状态(delay 和 timeout 无效)

defineAsyncComponent() 也支持在高级选项中处理加载和错误状态:

javascript 复制代码
const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),

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

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

下面,让我们来看一个完整示例。

App.vue:

javascript 复制代码
<template>
  <div id="app">
    <button @click="loadComponent('A')">访问A组件</button>
    <button @click="loadComponent('D')">访问D组件</button>
    <component :is="currentComponent" v-if="currentComponent"></component>
  </div>
</template>

<script setup>
import { shallowRef, defineAsyncComponent } from 'vue'
import LoadingComponent from '@/components/LoadingComponent.vue'
import ErrorComponent from '@/components/ErrorComponent.vue'
const currentComponent = shallowRef(null)

const loadComponent = (name) => {
  currentComponent.value = defineAsyncComponent({
    // 加载函数
    loader: () => import(`@/components/${name}.vue`),

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

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

</script>

<style lang="scss" scoped>
button {
  margin-right: 10px;
}
</style>

A.vue:

javascript 复制代码
<template>
  <div>这是A组件</div>
</template>

<script setup>

</script>

<style lang="scss" scoped>

</style>

LoadingComponent.vue:

javascript 复制代码
<template>
  <div>假装我在loading...</div>
</template>

<script setup>

</script>

<style lang="scss" scoped>

</style>

ErrorComponent.vue:

javascript 复制代码
<template>
  <div>这是加载失败后展示的组件</div>
</template>

<script setup>

</script>

<style lang="scss" scoped>

</style>

D.vue 未创建。

按理说,如果我点击 "访问A组件" 按钮,会有 3s 中的时间显示加载组件,然后在切换到A组件。但是实际上,却马上切换到了A组件。

按理说,点击D组件,应该也会经历 3s 的延迟后,再显示错误组件。但是实际上却马上显示了报错组件。

这可能需要搭配 <Suspense> 组件才会生效,让我们届时在进行进一步的学习。


上一章 《Vue3 重构待办事项(主要练习组件化)

相关推荐
cat10month4 天前
react-loadable懒加载使用
懒加载
linweidong5 个月前
汇量科技前端面试题及参考答案
webpack·vue3·react·前端面试·hooks·懒加载·flex布局
Samdy_Chan7 个月前
同时支持Vue2/Vue3的图片懒加载组件(支持懒加载 v-html 指令梆定的 html 内容)
前端·vue·vue3·vue2·懒加载·图片懒加载·图像懒加载
大模型铲屎官9 个月前
【HTML性能优化】提升网站加载速度:GZIP、懒加载与资源合并
前端·性能优化·html·gzip·懒加载·网站加载·资源合并
GJWeigege1 年前
如何实现图片懒加载,原生 + React 实现方式
前端·react.js·前端框架·懒加载·图片懒加载优化·列表优化
Play_Sai1 年前
鸿蒙ArkTS实用开发技巧: 提高效率的关键知识点
网络请求·harmonyos·arkts·懒加载·钩子函数
会思想的苇草i1 年前
HTML深度探索 :img应用与实践
前端·html·图片·懒加载·预加载·img
Eveweiscsdn1 年前
视频懒加载
前端·javascript·react.js·视频·video·懒加载
最小生成树1 年前
前端开发-- Webpack 代码分割和懒加载技术
javascript·vue·懒加载