背景
在实际开发中,封装组件是一个常见需求,尤其是在处理灵活配置的场景时。比如,一个通用表单组件需要支持动态配置表单项,并需要配置配置数据引入不同类型组件,包括自定义组件。这样的需求需要设计低耦合、高灵活性的方案。
示例场景
假设我们有一个通用表单组件 CForm
,通过 config
配置表单项:
<CForm :config="config" />
const config = [
{ component: 'el-input', label: '名称', key: 'name' },
{ component: 'myComponent', label: '自定义组件', key: 'custom' },
];
config
数组的每一项代表一个表单字段,其中 component
指定了要渲染的组件名称。然而,这种实现方式有一个 潜在问题:
- Vue 项目通常使用插件(
unplugin-vue-components
)实现按需引入。 - 这种方式在编译时会根据使用的组件进行打包,但运行时无法解析通过字符串形式指定的组件名称。
- 在组件内部使用
import 静态导入
可能被使用到的组件是可行的,但如果运行时并未实际使用这些组件,每次使用CForm
时,依赖的静态导入组件仍会被加载进来。这会导致资源浪费,并增加页面的初始加载时间
。
为了解决这个问题,通过 import
动态加载组件。
动态加载
CForm
组件内部
<template>
<div v-for="item of props.config">
<component :is="item.component" />
</div>
</template>
const checkComp = () => {
let components = props.config.map(x => x.component)
for (let com of components){
import(./../components/${com}.vue).then((module) => {
// 这里可以给组件命名,这个技巧可以用在动态路由需要缓存,但是不想每个页面组件命名上。
// 因为通常接口返回菜单和对应的页面组件。
})
}
}
// config数据变动。 不用担心会重复加载,因为加载的模块浏览器会进行缓存。
watch(() => props.config,() => {
checkComp()
})
总结
动态加载组件的实现
利用 import()
方法,在运行时根据组件名称动态加载模块。
ES Module缓存机制
import()
加载的模块会被浏览器缓存,即使多次加载相同路径,也不会重复发起请求。这一特性可以有效避免性能开销。
也可以内部维护一个加载过的组件的map
,判断是否加载过再决定是否import
。
动态渲染组件
使用 Vue 提供的 <component>
标签,结合 :is
动态绑定属性,将已加载的组件进行渲染。