1、问题描述

在Vue或React项目中经常会遇到比如下拉框的数据字典之类的,是在页面创建时获取数据字典,然后解析显示。
不同的页面使用同一个数据字典或者页面重复打开时都会发送多次相同请求,会造成时间和性能的浪费。
数据字典的解析在页面中使用重复率高,模式统一,在各个页面内存在大量重复代码,非常不符合代码复用思想,我们可以利用“混入”提升代码复用率。

简单的说,我们需要实现两点:
1.将请求过的数据字典存起来,下次再使用时不再发送后台请求
2.简化页面代码的操作过程,页面直接获取结果使用,不再关心过程

2、解决方案

使用vuex Store对数据字典数据进行存放
使用mixin提升代码复用,mixin可以用来更高效的实现组件内容的复用

这里vuex配置和使用不在进行介绍,默认项目中配置好了vuex,下面简单介绍一下mixin

官方文档:
  通过创建 Vue 组件,我们可以将界面中重复的部分连同其功能一起提取为可重用的代码段。仅此一项就可以使我们的应用在可维护性和灵活性方面走得相当远。然而,我们的经验已经证明,光靠这一点可能并不够,尤其是当你的应用变得非常大的时候——想想几百个组件。处理这样的大型应用时,共享和重用代码变得尤为重要。
  混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

文档地址:https://v3.cn.vuejs.org/guide/mixins.html

全局混入更为便捷,我们将不用在子组件声明,全局混入将会影响每一个组件的实例,使用的时候需要小心谨慎;这样全局混入之后,我们可以直接在组件中通过this.变量/方法来调用mixin混入对象的变量/方法;

mixin是在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件,简单来说有点类似于java中的继承

介绍完毕,接下来开始操作,首先创建以下文件:
mixin/metaDataFactoryMixin.js
store/modules/app.js

import store from '@/store'

/**
 * 设定自定义属性metaDataType: 用于声明定义组件内使用到的字典表
 *
 * 在metaDataType中声明的字典表可以直接通过metaData和metaDataMapper使用
 *
 * @author liaoyuxing
 * @date 2021-06-08
 */
const metaDataFactoryMixin = {
  created() {
    let metaDataType = this.$options.metaDataType || null
    if (metaDataType) {
      metaDataType = Object.prototype.toString.call(metaDataType) === '[object Array]' ? metaDataType : [metaDataType]
      metaDataType = metaDataType.map((item) => item.trim())
      localStorage.setItem('metaDataType', JSON.stringify(metaDataType))
      store.dispatch('app/registerMetaDataItems', metaDataType)
    }
  },
  computed: {
    metaData() {
      return store.getters.metaData
    }
  },
  methods: {
    metaDataMapper(key) {
      if (Array.isArray(key)) {
        let all = key.reduce((result, item) => {
          result = Object.assign(result, store.getters.metaDataMap(item) || {})
          console.log('result', result)
          return result
        }, {})
        return all
      }
      return store.getters.metaDataMap(key)
    },
    metaDataFilter(params) {
      return store.getters.filterMetaData(params)
    }
  }
}
export {
  metaDataFactoryMixin
}

import Cookies from 'js-cookie'
import Vue from 'vue'
// 获取字典值的接口
import { getDictsList } from '@/api/system/dict/data'

const state = {
  metaData: {}
}

const mutations = {
  ADD_META_DATA_ITEMS: (state, items) => {
    Object.keys(items).forEach((key) => {
      let value = items[key].map((item) => {
        let { dictCode, dictValue, dictLabel } = item
        return { dictCode, dictValue, dictLabel }
      })
      Vue.set(state.metaData, key, value)
    })
  }
}

const actions = {
  registerMetaDataItems({ commit, state }, items) {
    let types = items.filter((item) => !state.metaData[item])
    if (types && types.length) {
      getDictsList(types.join(',')).then((response) => {
        commit('ADD_META_DATA_ITEMS', response.data)
      })
    }
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

注意这里store.dispatch的地址是’app/registerMetaDataItems’
对应的是app.js中命名空间选项开启的路径

export default {
  namespaced: true
}

当命名空间选项关闭时,对应路径应为’registerMetaDataItems’

接下来将两个文件注册
在store/index中加入app.js

import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import getters from './getters'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    settings
  },
  getters
})

export default store

在main中加入metaDataFactoryMixin进行全局混入

// 混入字典方法
import {metaDataFactoryMixin} from './mixin/metaDataFactoryMixin'
...
Vue.mixin(metaDataFactoryMixin);

在store/getters中添加字典的获取和解析方法

  metaData: (state, getters) => {
    return state.app.metaData || {}
  },
  filterMetaData: (state, getters) => {
    return (params) => {
      if (typeof params === 'object') {
        let metaData = getters.metaData[params.key] || []
        return metaData.filter((item) => params.filter.some(key => key === item.dictLabel))
      } else {
        return getters.metaData[params] || []
      }
    }
  },
  // 字典数据
  metaDataMap: (state, getters) => {
    return (key) => {
      let sourceMap = {}
      if (getters.filterMetaData(key)) {
        getters.filterMetaData(key).forEach((item) => {
          sourceMap[item.dictValue] = item.dictLabel
        })
      }
      return sourceMap
    }
  },

搭建完毕
下面开始使用
让我们看看组件中需要做哪些改动:
1.声明页面需要使用的字典
在这里插入图片描述
2.为表格增加一个参数表示使用某一字典,修改类型为Map
在这里插入图片描述
3.增加type为Map的判断,当为字典时,进行解析处理
在这里插入图片描述
以上代码改为↓

在这里插入图片描述
接下来验证结果
在这里插入图片描述
OK,显示成功

同理,页面中的下拉框也可以使用此方法
接下来试一试下拉框的使用
在这里插入图片描述
将解析metaDataMapper改为metaData既可

在这里插入图片描述
OK,显示正常
让我们来看一下metaData和metaDataMapper的区别,首先分别看一下它们的输出结果

console.log(JSON.stringify(this.metaData['sys_approve_status']))

输出结果:

[
  {
    "dictCode": 95,
    "dictValue": "0",
    "dictLabel": "未审批"
  },
  {
    "dictCode": 96,
    "dictValue": "1",
    "dictLabel": "已审批"
  },
  {
    "dictCode": 97,
    "dictValue": "2",
    "dictLabel": "审批驳回"
  }
]

metaData返回结果为list

console.log(JSON.stringify(this.metaDataMapper('sys_approve_status')))

输出结果:

{
  "0": "未审批",
  "1": "已审批",
  "2": "审批驳回"
}

metaDataMapper返回结果为map
我们可以针对不同的应用场景来选择使用metaData和metaDataMapper,普遍来讲metaData适用于下拉框,而metaDataMapper适用于表格解析

总结:
通过Mixin全局混入的方式,我们可以在任意一个组件中使用metaDataMapper和metaData方法来获取字典列表和值,在组件中使用metaDataType声明需要使用的字典,将自动判断Store是否有缓存,动态向后台发送请求获取。
简单来说,我们不必关心过程,只需要组件使用metaDataType声明需要的字典,然后使用metaDataMapper和metaData获取字典就可以了

Logo

前往低代码交流专区

更多推荐