[vue3 vs vue2] Async Components

2,205 阅读3分钟

前言

当项目越来越大当时候我们需要考虑性能优化,一般采用两种方式。

  • code splitting (webpack 做的事)
  • lazy loading (延迟加载依赖直到需要加载的时候) 来看下在 vue2.x 和 vue3.x 中如何使用异步组件

vue2.x 异步组件

在 vue 2.x 就支持异步组件,在看异步组件之前我们来看下普通的静态引入的方式使用的组件。

静态引入

  Import Book from "./components/Book"
   export default {
     components: {
       Book
     }
   }

上面代码中,无论是否用到 Book 组件,都会加载该组件。

路由懒加载

平时最常用的就是路由懒加载,用到了

  1. Vue的异步组件
  2. 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文件,符合预期。

参考

Async Vue.js Components
路由懒加载
createElement 参数