前言
当项目越来越大当时候我们需要考虑性能优化,一般采用两种方式。
- code splitting (webpack 做的事)
- lazy loading (延迟加载依赖直到需要加载的时候) 来看下在 vue2.x 和 vue3.x 中如何使用异步组件
vue2.x 异步组件
在 vue 2.x 就支持异步组件,在看异步组件之前我们来看下普通的静态引入的方式使用的组件。
静态引入
Import Book from "./components/Book"
export default {
components: {
Book
}
}
上面代码中,无论是否用到 Book 组件,都会加载该组件。
路由懒加载
平时最常用的就是路由懒加载,用到了
- Vue的异步组件
- webpack的代码分割(使用 Dynamic imports 会自自动分割)
const router = new VueRouter({
routes: [
{ path: '/foo', component: () => import('./Foo.vue') }
]
})
由于我们使用了Dynamic imports 的方式在路由中引入页面, webpack 会根据每个页面去生成以页面为单位的 chunk 。如下图:
当我们切换页面的时候会动态加载对应的chunk
组件内部动态组件(不带选项)
除了在路由中动态引入组件,也可以在组件中动态引入。引入的方式和路由中定义一样,定义一个函数返回 import()。
由于我们要测试动态组件的效果,这里使用 v-if 来控制组件一开始不显示,当我们点击开关按钮的时候再去显示这个组件。
<template>
<div>
<Dynamic v-if="isShow"></Dynamic>
<button @click="isShow = !isShow">开关</button>
</div>
</template>
<script>
export default {
components:{
Dynamic:()=>import('./dynamic.vue')
},
data() {
return {
isShow:false
}
},
}
</script>
当我们点击按钮的时候查看 network 文件加载中多了一个 js 文件。和我们的预期相符。
组件内部动态组件(带选项)
除了直接 import() 一个组件以外,我们还可以设置一些选项。语法结构: ()=>{component:Promsie,loading:Component,delay:Number}
import Loading from './loading'
import SadFace from './SadFace'
export default {
components:{
Dynamic:()=>({
component:new Promise(res=>{
setTimeout(() => {
res(import('./dynamic.vue'))
}, 3000);
}),
loading:Loading, // 可选
delay:500, // 可选
timeout:1000, // 可选
error:SadFace // 可选
})
}
}
- component: 就是我们动态引入的组件,这里为了模拟异步组件,使用定时器3s后再返回对应组件。
- loading: 是设置提示器组件,当异步组件引入成功会消失。
- 该组件是同步引入的
- delay: 设置一个时间,异步组件在规定时间内未显示则展示 loading 组件。
- timeout: 设置一个时间,超时后则显示 error 处设置的组件
- error: 错误时候显示的组件,错误情况如下:
- 断网
- 超过 timeout 时间
- 组件不存在
vue3.x 异步组件
使用 defineAsyncComponent 用于显式地定义异步组件,同样支持带选项和不带选项的两种方式。
- 不带选项的: defineAsyncComponent(() => import('./asy1.vue') )
- 带选项的:
- 这里使用 loader 来代替vue2.x的 component
- loadingComponent 代替vue2.x的 loading
- errorComponent 代替vue2.x的 error
- 其他参数 delay、timeout与之前的使用相同
defineAsyncComponent({
loader:()=> import('./asy2.vue'),
delay:200,
})
完整实例如下:
<template>
<div>
<Asy1 v-if="isShowAsync1"></Asy1>
<button @click="isShowAsync1 = !isShowAsync1">async1 开关</button>
<Asy2 v-if="isShowAsync2"></Asy2>
<button @click="isShowAsync2 = !isShowAsync2">async2 开关</button>
</div>
</template>
<script lang='ts'>
import { defineAsyncComponent,defineComponent } from 'vue';
const Asy1 = defineAsyncComponent(() => import('./asy1.vue') )
const Asy2 = defineAsyncComponent({
loader:()=> import('./asy2.vue'),
delay:200,
})
export default defineComponent({
components:{
Asy1,
Asy2,
},
data() {
return {
isShowAsync1:false,
isShowAsync2:false
}
}
});
</script>
当我们点击开关后同样会动态加载该组件的js文件,符合预期。