vue性能优化之异步组件

        优化方案适用于首屏加载之类的,初始化出现白屏的情况,当一个工程庞大到一定程度的时候,不去做异步分割的话,会导致初始化越来越慢。Chrome的控制台里可以看到Dom渲染的时间。

我们新建一个入口页面,引入一个子组件,然后在template中使用。

<template>
    <div class="costApplyIndexBox">
      测试
      <child></child>
    </div>
</template>

<script lang="ts">
    import { Component, Vue } from "vue-property-decorator";
    import child from "./childComponents.vue";
    @Component({
        components: {
          child
        },
        mixins: []
    })
    export default class testIndex extends Vue{

        created(){
        }

    }
</script>

这时我们去页面上面去查看,页面的加载情况,

页面上面显示只加载了一个js文件,且是我们的主入口文件,我们直接引入的child是被打包进主入口文件里面去了,跟着主入口一起被加载了。事实上,很多场景并不需要子组件和入口一起进行加载的。这个时候我们就需要对子组件进行拆分了。

什么是异步组件?顾名思义,他是一个异步加载的组件,并不是与入口组件同步加载的。

异步组件 | Vue.js

 这样,异步组件的存在,可以使得我们按需使用与加载。我们应该如何使用异步组件?Vue提供了 defineAsyncComponent  方法供我们实现。

首先现在vue.config.js中配置一下分割的条件。

        optimization:{
            splitChunks:{
                chunks: 'all', // 分割哪些类型的chunk,默认为'async',可选值有'all'、'async'、'initial'和function
                minSize: 30000, // 单个chunk的最小体积,单位为字节(bytes),默认为30000(约30KB)
                maxSize: 0, // 单个chunk的最大体积,0表示无限制,默认为0
                maxAsyncRequests: 30, // 并行加载时最大允许的请求数量,默认为5
                maxInitialRequests: 20, // 入口点加载时最大允许的请求数量,默认为3
                automaticNameDelimiter: '~',//文件之间链接的符号
            }
        }

这里就不对splitChunks做过多的解释了,它实在是太有趣了,三言两语讲不清楚,只粗略的讲一下几个关键的参数。感兴趣的可以直接去webpack:详解代码分离以及插件SplitChunksPlugin的使用 中好好体会(强烈建议去观看)。

我们在刚刚的代码上引入 defineAsyncComponent ,将子组件通过它来引入。

<script lang="ts">
    import {defineAsyncComponent} from 'vue'
    import { Component, Vue } from "vue-property-decorator";
    const child = defineAsyncComponent(() => import('./childComponents.vue'))
    @Component({
        components: {
          child
        },
        mixins: []
    })
    export default class testIndex extends Vue{

        created(){
        }

    }
</script>

如你所见,defineAsyncComponent  方法接收一个返回 Promise 的加载函数。这个 Promise 的 resolve 回调方法应该在从服务器获得组件定义时调用。你也可以调用 reject(reason) 表明加载失败。

ES 模块动态导入也会返回一个 Promise,所以多数情况下我们会将它和 defineAsyncComponent  搭配使用。类似 Vite 和 Webpack 这样的构建工具也支持此语法 (并且会将它们作为打包时的代码分割点),因此我们也可以用它来导入 Vue 单文件组件。

这时我们再返回页面上面查看JS的加载情况。

多了一个js文件,他是我们异步加载的子组件,我们是可以对其进行命名的,我们只需要在import中添加一行注释。

//webpackChunkName:'' 拆分的文件名,这将会影响到后面打包生成的文件
const child = defineAsyncComponent(() => import(/* webpackChunkName: 'page-child' */'./childComponents.vue'))

这时候子组件就拥有了自己的文件名。

这样体现不出异步加载的感觉,我们将其用v-if包裹起来,并且增加一个三秒后展示子组件的逻辑。

<template>
    <div class="costApplyIndexBox">
      测试
      <child v-if="showChild"></child>
    </div>
</template>

<script lang="ts">
    import {defineAsyncComponent} from 'vue'
    import { Component, Vue } from "vue-property-decorator";
    const child = defineAsyncComponent(() => import(/* webpackChunkName: 'page-child' */'./childComponents.vue'))
    @Component({
        components: {
          child
        },
        mixins: []
    })
    export default class testIndex extends Vue{
        private showChild:boolean = false
        created(){
          setTimeout(()=>{
            this.showChild = true;
          },3000)
        }

    }
</script>

再次回到页面上去,页面初始化并没有加载我们的“page-child”。

三秒之后,子组件展示,并执行“page-child”js文件,这时我们异步组件就拆分完成了。

接下来讲一些使用方法的拓展。异步组件再被引入之后他同样是一个组件对象,是可以被component标签识别的。这样做的好处就是无需在components中注册它。

<template>
    <div class="costApplyIndexBox">
      测试
      <component :is="child"></component>
    </div>
</template>

<script lang="ts">
    import {defineAsyncComponent} from 'vue'
    import { Component, Vue } from "vue-property-decorator";
    @Component({
        components: {
          // child
        },
        mixins: []
    })
    export default class testIndex extends Vue{
        private showChild:boolean = false
        private child:any = ''
        created(){
          setTimeout(()=>{
            this.child = defineAsyncComponent(() => import(/* webpackChunkName: 'page-child' */'./childComponents.vue'))
            this.showChild = true;
          },3000)
        }

    }
</script>

在setup中甚至无需使用component标签,直接可以在template中使用它。

<template>
    <div class="costApplyIndexBox">
      测试
      <component :is="child"></component>
      <child></child>
    </div>
</template>

<script setup lang="ts">

    import { defineAsyncComponent,onMounted} from "vue";
    // import { Component, Vue } from "vue-property-decorator";
    const child = defineAsyncComponent(() => import(/* webpackChunkName: 'page-child' */'./childComponents.vue'))

    onMounted(() => {
      console.log(child);
    })
</script>

这里要注意的是 defineAsyncComponent  引入的组件,并非为全局组件,它的作用域仅在当前组件内部,并不是说不需要再components中注册就变成了全局组件。

当然他可以被注册为全局组件。

<template>
    <div class="costApplyIndexBox">
      测试
      <component :is="child"></component>
      <child></child>
    </div>
</template>

<script lang="ts">
    import {defineAsyncComponent} from 'vue'
    import { Component, Vue } from "vue-property-decorator";
    @Component({
        components: {
          // child
        },
        mixins: []
    })
    export default class testIndex extends Vue{
        private showChild:boolean = false
        private child:any = ''
        created(){
          setTimeout(()=>{
            this.child = defineAsyncComponent(() => import(/* webpackChunkName: 'page-child' */'./childComponents.vue'))
            Vue.component('child',this.child)
            this.showChild = true;
          },3000)
        }

    }
</script>

使用 Vue.component('组件名',组件对象) 即可挂成为全局组件。这种方法尽量少挂,或者不挂,保不齐会出现组件名重复冲突的情况等等。不想每个组件都写一遍import,同样可以建一个组件仓库的ts文件,在里面提前引入好,只用import一个文件同样可以使用。

componentsHome.ts

export default {
    child:() => import(/* webpackChunkName: 'page-child' */'./childComponents.vue')
}

这里没有使用 defineAsyncComponent 。是因为我发现import返回的也是一个组件对象。

在我翻阅完了 defineAsyncComponent 的源码的时候,发现了它最后return了一个对象。

            return function() {
                return {
                    component: f(),
                    delay: i,
                    timeout: c,
                    error: r,
                    loading: n
                }
            }

这里的 f() 事实上就是我们传入的 import() 函数。

  • loader: 用于加载组件的函数,返回一个Promise。
  • loadingComponent: 加载中显示的组件。
  • errorComponent: 加载失败时显示的组件。
  • delay: 开始显示加载指示器前的延迟时间。
  • timeout: 组件加载的超时时间,超过此时间则视为加载失败。

它提供了更细致化的配置,可以使我们的异步组件更加完善。同理无法使用defineAsyncComponent 的低版本Vue 可以手动封装这样的一个对象去使用。

以上只是本人对于异步组件的一些理解,有错误的地方的话,欢迎大家评论区交流与指点。

  • 24
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值