前言
我们在业务开发中,大量字典选择场景,于是产生了个想法 —— 能否封装一个通用型的字典选择组件呢。
期望
- 组件支持select/radio/checkbox,并可扩展不同的UI选择组件;
- 组件字典数据缓存,避免重复接口请求;
- 相同字典组件同时加载,请求并发(n个相同类型的字典同时加载)
实现
这里使用Vue实现,基于ElementUI组件库,但不限于此库。有如下结构:
|-- dict
|-- config.js // 配置组件映射表
|-- DictNormal.vue // 组件文件
|-- mixins.js // 通用逻辑可抽离出来,方便后续独立使用
动态组件
我们期望可以支持select/radio/checkbox等交互组件,使用component
实现动态组件。分析选择组件应用时的模板结构,模板结构可约定为:
component is="el-select"
component is="el-option"
动态组件可以通过is来设定,组件列表配置在config.js
中:
// config.js 结构示例
export const tagsMap = {
'select': {
tag: 'el-select',
optionTag: 'el-option',
optAttrs: {
label: 'value',
value: 'key',
},
},
'checkbox': {
tag: 'el-checkbox-group',
optionTag: 'el-checkbox',
optAttrs: {
label: 'key',
inner: 'value',
},
},
}
组件如下:
<template>
<component
:is="tagIns.tag"
v-on="listeners"
v-bind="$attrs"
@change="handleChange"
>
<component
:is="tagIns.optionTag"
v-for="(opt, idx) in dict"
:key="opt.key + '-' + idx"
v-bind="genOptAttrs(opt)"
>
<slot
v-if="genOptAttrs(opt).inner"
:name="`option_${opt.key}_${idx}}}}`"
v-bind="genOptAttrs(opt)"
>
{{ genOptAttrs(opt).inner }}
</slot>
</component>
</component>
</template>
<script>
import Mixins from './mixins';
import { tagsMap } from './config';
export default {
name: 'DictNormal',
mixins: [Mixins],
props: {
// 组件类别
tag: {
type: String,
default: 'select',
},
// 定制选项标签上挂载的属性
optAttrs: {
type: [Function, Object],
},
},
computed: {
tagIns() {
let hit = tagsMap[this.tag];
return hit;
},
},
methods: {
/**
* ignore
*/
genOptAttrs(itm) {
if (typeof this.optAttrs == 'function') {
return this.optAttrs.call(this, itm) || {};
}
let attrs = {};
let optAttrs = this.tagIns.optAttrs;
if (typeof this.optAttrs == 'object') {
optAttrs = this.optAttrs;
}
for (let k in optAttrs) {
attrs[k] = itm[optAttrs[k]];
}
return attrs;
},
},
};
</script>
字典数据缓存
缓存位置暂只考虑挂在该组件,也可自定挂载位置。
// mixins.js
const cacheDict = {}; // 缓存
export default {
props: ['dictType'],
data() {
return {
dict: [], // 字典选项数据
}
},
methods: {
getDict(type){ return cacheDict[type] },
fetchDictList(type) { /* 接口请求 */ },
/** 处理响应数据 */
responseHandler(res) {
if (res.error === 0) {
this.commit(this.dictType, res.data);
}
},
/** 设置数据 */
commit(key, data = []) {
this.$emit('on-data', data, key);
cacheDict[key] = data;
this.dict = [...data];
},
}
}
同字典多个并行加载
现有dict-type="grade"
字典组件,在表单有两个地方用到,如下:
el-form
el-form-item prop="onlineGrade"
DictNormal dict-type="grade"
el-form-item prop="offlineGrade"
DictNormal dict-type="grade"
模拟一下,页面初始加载:
可以看到,请求多次触发。为解决这个问题,第一次请求发出,需要做promise初始设置,第二次请求的时候,如果promise已经存在,则直接发送。这时候的promise需要映射到字典类型上。关键代码如下:
// mixins.js
import axios from 'axios';
const cachePromise = {};
// 接口模拟方法
const fetchDict = ({type}) => axios.get('/dict', params: { type })
export default {
props: ['dictType'],
created() {
this.initPromise(this.dictType);
},
async initPromise(dictType) {
if (!cachePromise[dictType]) {
this.__promise = null;
cachePromise[dictType] = this.fetchDictList(dictType);
}
this.__promise = this.__promise || cachePromise[dictType];
this.responseHandler(await this.__promise);
},
/** 拉取字典数据 */
async fetchDictList(dictType) {
if (this.__promise && this.cache) {
return await this.__promise;
}
return await fetchDict({ type: dictType });
},
/** 处理响应数据 */
responseHandler(res) {
if (res.error === 0) {
this.commit(this.dictType, res.data);
}
},
}
总结
大致的思路就是上面所述的:
Promise
的使用,解决多个相同的请求触发- 缓存来减少重复请求
component
动态选用组件
如果拓展其他字典交互选择组件(比如tag,icon等),可以开发好后内层、外层组建后,引用配置到config中以供使用。更甚者复用mixins,来快速实现缓存、同时请求等字典组件的基础能力。
感谢阅读,有问题欢迎讨论~
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!