字典组件作为常用组件,好好封装一下很有必要。
使用字典的常见方式
- 字典数据作为 JSON 文件在前端项目中引入;
- 应用加载时,把所有字典的数据请求过来缓存在前端;
- 根据需要按需调用接口获取字典数据,然后渲染组件;
方式 1 非常不灵活,适用于不会变动的字典。要不后端数据一变化,前端还要跟着维护,这样容易出问题。
方式 2 缺点也比较明显:
- 把数据库所有字典数据请求过来,安全性不高(不想暴露的字典也被拿出来了),而且有的系统字典数据非常大。
- 不会实时更新,一般是在用户重新登录或者刷新页面后才能更新
在我的 mocha Vue3 Admin 中很早就封装了字典组件,但是感觉并不理想。
之前的做法
- 使用 useDict 钩子在渲染数据前通过接口调用字典API获取需要字典的数据(以此避免多个相同的字典组件多次请求接口)
- Modict 组件根据返回的数据渲染字典的文字和样式
以上方法最大的缺点是:虽然使用 useDict 减少了请求,但是每次切换页面都会重新调用API,增加网络请求。另外,因为每次都要先调用useDict,用起来还是稍嫌麻烦。
优点是可以实时读取字典,不会出现滞后。
新方案
所以趁着假期,继续优化我们的字典组件。
因为在实际场景中,大部分字典的值是几乎不会变动的。所以把字典数据缓存在前端没问题,对于需要实时更新也让它能够更新起来。
接下来,就是实现下面 4 个需求:
- 按需加载数据,只请求需要的字典数据
- 无需频繁更新的数据,保存在缓存中
- 需要实时更新的数据,可以保持及时更新
- 减少请求次数
需求 1 根据当前字典的 name 返回字典数据即可。
需求 2,我们先判断缓存中有没有当前字典的数据,如果有,直接用缓存的,反之,调用接口后再放入缓存中,这样可以极大减少网络请求。
在表格、列表这种,同一个字典标签被重复使用几十次,在第一次进入还没有缓存时,依然会向后端发送多次请求。需求 3:实时更新数据,这种情况不再依赖缓存,每个字典标签都会发出一个请求。 所以如何减少重复请求呢?当然少不了好用的 promise。
首先,我们使用pinia来管理字典,在 store 下新建 dict.ts,
主要的两个方法代码如下:
注:在此使用 piniaPluginPersistedstate 将 pinia 的 字典数据保存在缓存中,本项目使用SessionStorage。
可以在应用重新加载/重新登录时清除缓存,重新获取字典数据。
// ...
export const useDictStore = defineStore(
'dict',
() => {
const dicts = ref<DictMap>({
})
const getDictData = async (dictType: string, refresh: boolean = false) => {
return new Promise((resolve, reject) => {
let data: any = dicts.value[dictType]
// 根据缓存中是否有数据 && refresh(==false),读取缓存中的数据
if (data &&