动态组件和异步按需加载

536 阅读1分钟

在软件解决方案中,存在三七定律,vue也不例外。通俗的讲,就是30%的基本API能解决70%的问题,剩下30%的问题就需要非常规性方法解决。今天我们要讲的也算是一种特殊场景,需要使用到一些非常规性手段。

业务场景

  1. 最常接触到的一种场景就是通过鼠标拖拽组件生成h5页面,类似积木。
  2. 活动模板根据接口数据决定展示哪几个组件

这类场景我的疑问是:

如果组件库有100个组件,我拖拽的生成的页面中只使用了其中5个组件,如何保证最终webpack打包生成的代码只含有这5个组件的代码,不包含其他冗余代码?

动态组件

文件目录结构

.
├── component
│   ├── LoadSyncComps.js
│   ├── TestA.vue
│   └── TestB.vue
└── index.vue
 <!--TestA.vue-->
 
<template>
    <div>{{name}}</div>
</template>

<script>
export default {
    name: 'TestA',
    data() {
        return {
            name: 'testA模块'
        }
    }
}
</script>


<!--TestB.vue-->

<template>
    <div>{{name}}</div>
</template>

<script>
export default {
    name: 'TestB',
    data() {
        return {
            name: 'testB模块'
        }
    }
}
</script>

require + component 动态加载组件方案:

<!--index.vue-->

<template>
    <div>
        <component v-for="(item, index) in ary" :is="item.app" :key="index"></component>
    </div>
</template>

<script>
export default {
    data () {
      return {
        comps: ['TestA'],
        ary: []
      }
    },
    created () {
        this.loadComp();
    },
    methods: {
        loadComp() {
            this.comps.forEach(app => {
                 this.ary.push({app: require(`./component/${app}.vue`).default})
            })
        }
    }
}
</script>

这种方式是require + component结合实现的,利用require实现动态加载,component做组件切换。

但是这种方案只实现了动态加载功能,并未实现按需加载。通过查看打包后的代码,会发现TestB.vue未被引用,但是也被打包进去了。require是CommonJS规范,在同路径下的vue文件,都会被打包进去。如果组件不是特别多的情况下,这种方法可以满足大部分场景。

动态组件 & 按需加载

可以使用ES6中的import方法,该方法不同于import A from B,这种属于纯静态编译,无法实现动态加载。为了实现动态加载,引入了import()方法,该方法属于动态编译,webpack在打包时,碰到import()方法,会单独生存一个独立文件,用于按需加载。

import + component 动态加载组件方案:

<!--index.vue-->

<template>
    <div>
        <component v-for="(item, index) in ary" :is="item.app" :key="index"></component>
    </div>
</template>

<script>
export default {
    data () {
      return {
        comps: ['TestA'],
        ary: []
      }
    },
    created () {
        this.loadComp();
    },
    methods: {
        loadComp() {
            this.comps.forEach(app => {
                 this.ary.push({app: () => import(`./component/${app}.vue`)})
            })
        }
    }
}
</script>

除了使用vue提供的component,我们还可以使用Vue.extend自己封装动态组件

<!--LoadSyncComps.js-->

import Vue from 'vue';

const LoadSyncComps = Vue.extend({
    props: {
        comps: {
            default: []
        }
    },
    render(createElement) {
        const list = [];
        this.comps.forEach(item => {
            const comp = require(`./${item}.vue`).default
            <!--或者使用import-->
            <!--const comp = () => import(`./${item}.vue`);-->
            list.push(createElement(comp));
        });
        return createElement('div', list);
    }
})

export default LoadSyncComps;
<!--index.vue-->

<template>
    <div>
        <LoadSyncComps :comps="comps"/>
    </div>
</template>

<script>
import LoadSyncComps from './component/LoadSyncComps';
export default {
    components: {
        LoadSyncComps
    },
    data () {
      return {
        comps: ['TestA']
      }
    }
}
</script>

image.png