Vue 全局缓存 字典缓存 节流

前言

在后台管理系统中基本上都会有字典之类的操作。这类操作有一个共性:相同接口、不同参数返回不同的目标数据、数据结构一致。使用上无非是一些下拉选项、多选、状态翻译等。

有一个很实际的问题,A页面中存在某个下拉B页面也存在,一般的做法是两次请求,分别获取数据。但是当项目中字典数据应用越来越多的时候,大量重复的请求无法避免,造成资源浪费。

一般来说有两个解决办法:

1.随着项目启动将所有需要获取的数据获取一遍,缓存起来,用的时候直接取。优点是直观,方便管理。缺点是启动时的网络压力很大,用户可能不会点开很多页面,一定程度上资源浪费。

2.当需要的时候进行缓存,再次使用的时候直接取数据,不用再次请求。优点是灵活,按需就取。缺点是,依赖发布订阅模式(Vue中双向绑定),每次都需确认调用。

今天要说的是方法2的一种实现。

实现

直接上干货

import Vue from 'vue'
import Api from '@/api/index.js'
const cache = {
  namespaced: true,
  state: {
    dict: {},
    queue: []
  },
  mutations: {
    SET_DICT: (state, { type, list }) => {
      // 设置值:注意得用Vue.set方法
      Vue.set(state.dict, type, list)
    },
    SET_QUEUE: (state, type) => {
      state.queue.push(type)
    },
    DEL_QUEUE: (state, type) => {
      // 找到并删除
      for (let i = 0; i < state.queue.length; i++) {
        const item = state.queue[i]
        if (item === type) {
          state.queue.splice(i, 1)
          break
        }
      }
    }
  },
  actions: {
    // 缓存字典内容
    dict({ commit, state }, type) {
      // 判断是否已经缓存过,缓存过后将不再获取数据
      const isExistence = state.queue.some((item) => {
        return item === 'dict/' + type
      })
      // 没有缓存则请求数据
      if (!isExistence) {
        // 在异步请求之前将该任务标记为已缓存(存入队列)
        commit('SET_QUEUE', 'dict/' + type)
        return new Promise((resolve, reject) => {
          return Api.getDict(type)
            .then((res) => {
              const { code, data } = res
              if (code && code !== 200) {
                // 获取失败移除缓存队列
                commit('DEL_QUEUE', 'dict/' + type)
                reject(res)
              } else {
                // 获取到的值存入字典
                commit('SET_DICT', { type: type, list: data })
                resolve(res)
              }
            })
            .catch((error) => {
              // 获取失败移除缓存队列
              commit('DEL_QUEUE', 'dict/' + type)
              reject(error)
            })
        })
      } else {
        return state.dict[type]
      }
    }
  },
  getters: {
    getDict: (state) => {
      return state.dict
    },
    getDictByType: (state) => (type) => {
      /**
       * 闭包特性。返回一个函数,该函数接收一个type参数。
       * 同时由于闭包还持有state对象,故可以返回需要的值。
       */
      return state.dict[type]
    }
  }
}

export default cache

其中Api对象具体实现由各位自己定义,以及getDict函数是假定的,回调内容也可以根据具体的架构进行调整。

解析

上述代码到底实现了什么呢?缓存与队列

在异步编程中要考虑的是并发,如何才能避免同一数据多次请求的发生,是这个程序的一个重点。我采用了一个简单队列来避免,只要发起了就不再发起相同的请求,在成功时存入缓存,失败时清除队列里的数据。

这里还可以拓展一下,类似于失败后重试功能,思路大概是定时定次轮询,需要自行实现。

上述只是针对字典的缓存,但,只要符合相关共性的都可以集成到这个程序中,大同小异。

使用

实现一个与之相关的组件,字典下拉组件,采用的是Element UI。

<template>
  <el-select v-model="dataValue" :placeholder="getPlaceholder"
    style="width:100%" :disabled="disabled" :readonly="readonly" :size="size">
    <el-option v-show="!isRequired" :label="getPlaceholder" value="">
    </el-option>
    <el-option v-for="item in cacheDict[code]" :key="item.dm" :label="item.dmmc"
      :value="item.dm"></el-option>
  </el-select>
</template>

<script>
/** *
 * 字典字段过滤组件
 */
import { mapGetters } from 'vuex'
export default {
  name: 'DictSelect',
  components: {},
  props: {
    /**
     * 双向绑定
     */
    value: [String, Number, Boolean, Array, Object],
    /**
     * 字典编码
     */
    code: {
      type: String,
      required: true
    },
    /**
     * 描述
     */
    placeholder: {
      type: String,
      default: undefined
    },
    /**
     * 是否显示请选择
     */
    isRequired: {
      type: Boolean,
      default: false
    },
    /**
     * 是否禁用
     */
    disabled: {
      type: Boolean,
      default: false
    },
    /**
     * 是否只读
     */
    readonly: {
      type: Boolean,
      default: false
    },
    /**
     * 组件大小
     */
    size: {
      type: String,
      default: 'small'
    }
  },
  data() {
    return {
      dataValue: ''
    }
  },
  computed: {
    getPlaceholder() {
      return this.placeholder != undefined &&
        this.placeholder != 'undefined' &&
        this.placeholder != ''
        ? '请选择' + this.placeholder
        : '请选择'
    },
    ...mapGetters({ cacheDict: 'cache/getDict' })
  },
  watch: {
    value(val, oldValue) {
      if (val !== this.dataValue) {
        this.dataValue = val
      }
    },
    dataValue(val, oldValue) {
      if (val !== this.value) {
        this.$emit('input', val)
      }
    }
  },
  created: function () {
    this.dataValue = this.value
  },
  mounted: function () {
    this.getData()
  },
  methods: {
    async getData() {
      await this.$store.dispatch('cache/dict', this.code)
    }
  }
}
</script>

结合上述缓存程序,我们可以发现,在字典下拉组件中的 getData() 虽然每渲染一次都调用了,但由于 缓存程序中 dict 函数实现里会检测是否已经存在,所以不管他有没有返回值,这里都只是作为一个资源确认方式。

使用了vue的双向绑定,所以我们无需关心数据什么时候会有,有了自然会渲染。

而在页面中使用起来就更加方便

<DictSelect v-model="formData.dictType" code="106"></DictSelect>

总结

核心问题是节流,引申出弱业务关联的抽象实现。本质上是一种思路,具体方案还得根据具体框架实现。

将问题分解,然后以叠金字塔的方式逐步实现,最后塔尖为我们所用。

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
字典数据缓存可以通过将字典数据保存在Vuex状态管理中实现。在Vue3和TypeScript中,你可以按照以下步骤进行操作: 1. 在Vuex中创建一个模块来管理字典数据,可以使用`createStore`函数创建一个全局状态管理仓库。 2. 在该模块中定义一个状态变量来保存字典数据。 3. 创建一个Action用于异步请求字典数据,并将数据保存到状态变量中。你可以使用`axios`或`fetch`等工具发送HTTP请求。 4. 在组件中,使用`mapActions`辅助函数将Action映射到组件的方法中,以便在需要的地方调用。 5. 当组件需要使用字典数据时,先检查状态变量中是否已经存在数据。如果存在,直接使用;如果不存在,则调用Action去请求数据。 6. 可以将字典数据保存在本地缓存中,以便在页面刷新后仍然可以使用。可以使用`localStorage`或者`sessionStorage`来保存数据。 下面是一个示例代码,演示了如何在Vue3和TypeScript中实现字典数据缓存: ```typescript // 在vuex模块中定义字典数据的状态变量和请求方法 const dictionaryModule = { state: { dictionaryData: null, // 字典数据 }, actions: { async getDictionaryData({ commit }) { // 检查本地缓存中是否存在字典数据 const cachedData = localStorage.getItem('dictionaryData'); if (cachedData) { commit('setDictionaryData', JSON.parse(cachedData)); } else { // 发送HTTP请求获取字典数据 const response = await axios.get('/api/dictionary'); const data = response.data; commit('setDictionaryData', data); // 将数据保存在本地缓存中 localStorage.setItem('dictionaryData', JSON.stringify(data)); } }, }, mutations: { setDictionaryData(state, data) { state.dictionaryData = data; }, }, }; // 在组件中使用字典数据 export default { methods: { ...mapActions(['getDictionaryData']), async fetchData() { await this.getDictionaryData(); // 请求字典数据 // 使用字典数据 // ... }, }, created() { this.fetchData(); }, }; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值