关于字典项(下拉列表)在前后端的实践思考

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 最近接触了一点公司的后端代码,刚好自己有非常多的疑问,带着这些疑问,我就来看看公司后端的代码是如何实现的。由于工作中表单的录入工作相对频繁,而且有非常多的下拉选项,所以就先来看看字典项目的获取。

image.png


1、前言


上一篇文章算的话是9月20日发的,到今天11月10日,大致有50天没有发文了。这段时间当然也在学习,一方面解决自己的问题,另外一方面公司刚好也比较忙,到现在应该说是又可以回归正常了。那么还说什么呢?开始卷文章学习吧。


最近接触了一点公司的后端代码,刚好自己有非常多的疑问,带着这些疑问,我就来看看公司后端的代码是如何实现的。由于工作中表单的录入工作相对频繁,而且有非常多的下拉选项,所以就先来看看字典项目的获取。


不看不知道,一看吓一跳,后端们用了史上最简单粗暴的方式进行处理的。也就是每个下拉都会调用mysql


image.png


const  GetDictionaryData = (typeCode) => {
  // 通过sql返回列表数据
  select code, name from  dictionary where typeCode = typeCode
  // result = 返回列表
  return result
} 


然后前端怎么做呢?在用到字典的地方调用接口就完事了。对于一般的系统,用户量比较少,或者并发性能要求没那么高,慢点就慢点也就无所谓了。


这其中其实存在蛮多的问题?来看看我是怎么处理的呢?不能说我处理的好吧,只能说在现有基础上做了一些改进,有效的提升了一部分性能和方便性。


2、前后端的第一波调整


2.1、后端


这里其实我是将后端的字典项,存到了redis中,先简单看一下模拟方法


const GetDictionaryData = (typeCode) => {
   // 先判断redis中是否缓存
   if(redis.get(typeCode)) {
      return redis.get(typeCode)
   } else {
      // 先从数据库中获取
      var list = mysql查询返回结果
      redis.set(typeCode, list);
      return list;
   }
}


在后端简单将字典项目缓存到redis,这样有效的缓解了数据库的访问压力。不用每次都来调用数据库。


2.2、前端


前端的考虑跟后端有点类似,那就是缓存一下


const GetDictionaryData = async (typeCode) => {
    // 比如这里先来判断localStorage或者sessionStorage等等中是否缓存
    if(localStorage.getItem(typeCode)) {
       return JSON.parse(localStorage.getItem(typeCode))
    } else {
        const result = await getApi({typeCode})
        if(result.code === 200){
            localStorage.setItem(typeCode, JSON.stringify(result.data))
            return result.data
        }
    }
}


也就是在调用接口前,先判断浏览器缓存中是否存储了字典的数据项,如果有那么直接使用本地的,不用调用后端接口了,如果缓存不存在,则通过接口调用,并写入到本地缓存中。


3、前后端第二波更新


在第一波更新中存在一个小问题,如果后端数据更新了,后端的缓存数据也更新了,而我们前端还是拿的我们浏览器缓存的,那么数据就出现了非一致性的问题了。那现在我就来进行简单的调整。


3.1、后端


const GetDictionaryData = (typeCode,timeStamp) => {
   // 先判断redis中是否缓存
   if(redis.get(typeCode)) {
      var result = redis.get(typeCode)
      if(result.timeStamp != timeStamp) {
         return redis.get(typeCode)
      } else {
          return {
              timeStamp
          }
      }
   } else {
      // 先从数据库中获取
      var list = mysql查询返回结果
      var result = {
          list: list,
          timeStamp: 时间戳
      }
      redis.set(typeCode, result);
      return result;
   }
}


再来看一下前端代码的修改


3.2、前端


先说一下思路,因为害怕后端数据会更新,所以每次都进行调用接口来获取字典项目,但是多了一个参数,timeStamp,第一次的时候可以不传递。获取到接口返回的数据后还是要进行浏览器缓存存储。


const GetDictionaryData = async (typeCode) => {
    // 比如这里先来判断localStorage或者sessionStorage等等中是否缓存
    if(localStorage.getItem(typeCode)) {
        let cache = JSON.parse(localStorage.getItem(typeCode))
        const result = await getApi({
            typeCode: typeCode,
            timeStamp: cache.timeStamp
        })
        if(result.code === 200 ) {
            // 如果本地时间戳与接口返回的时间戳一致
            // 则后端只返回时间戳,字典列表项就不返回了
            // 直接使用前端缓存的字典项
            if(result.data.timeStamp === cache.timeStamp){
                return cache.list
            } else {
                // 如果时间戳不一致,则代表后端接口数据返回可能发生变更了
                // 先更新缓存
                localStorage.setItem(typeCode, JSON.stringify(result.data))
                return result.data.list
            }
        }
    } else {
        // 则代表缓存没有数据
        const result = await getApi({typeCode})
        if(result.code === 200 ) { {
            localStorage.setItem(typeCode, JSON.stringify(result.data))
            return result.data.list
        }
    }
}


通过时间戳字段进行对比,时间戳相同,则代码钱后端数据一致,后端可以不传递list字典项目,只传递时间戳,方便与前端比对。前端传递的时间戳如果与后端的不一致,那后端就需要将字典项目list 和时间戳一起返回,前端需要更新浏览器缓存。


这里虽然每次都请求了服务器,但当字典第一次从数据库获取被缓存之后,就相当于只返回时间戳字段,而且对于获取的数据是读取的是redis缓存中的,对mysql数据库服务器的压力将大大减少。当然redis的作用也绝不仅仅就是缓存,还有很多其他更牛逼的功能。


4、可能还有第三波


如果系统够大,做的更精细化一些。是不是针对字典项目会有专门的地方进行维护,维护到mysql数据库的时候,同时会同步到redis缓存中,这样后端的缓存将会使用的更加到位吧?也只是我的猜测,实际的话要根据具体的业务需求来吧


5、总结


最近在公司折腾了一点后端,初步想法是最近半年允许的话,在公司项目的基础上多搞一下后端,全身心投入两年前端,自己感触也颇多,等年底有空的时候也来唠一唠。


  • 搞一搞mysql数据库性能方面的优化


  • 搞一搞redis在后端中的角色成分


  • 搞一搞比如rabbitMQ 消息队列


  • 搞一搞微服务相关的搭建构件


  • 当然还有其他的,比如Grpc、DDD、IOC、AOP、Docker、K8S、Cron、JWT、 等等吧,加油趁着现在还有折腾的欲望再卷一卷。


当然我搞的是比较偏门的语言:.net core,有兴趣的或者正在路上的咱们可以一起多交流。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
9月前
|
SQL 运维 数据管理
Dataphin补数据又双叒叕升级了,支持筛选节点类型,支持输入或粘贴节点名称批量补数据
Dataphin 4.0版本针对补数据操作进行了升级,旨在提升用户体验。在面对数据缺失问题时,如某企业因上游系统故障需紧急回刷历史数据,Dataphin提供了按节点类型筛选下游节点的功能,减少了手动操作的错误和时间消耗。对于大规模任务管理,如银行数据中心的历史数据补充,Dataphin支持按照节点名称批量补数据,提高了效率和准确性。此外,还优化了逻辑表补数据的性能,并允许配置超时任务自动重跑,以应对调度高峰。
139 1
|
9月前
|
存储 自然语言处理
平台设计-代码字段与标签
在平台里描述对象的属性可以使用代码和标签
【项目需求】:两个下拉框之间选项关联
一.利用Option Group Attributes中的disabled属性实现 二.利用下拉框的@change事件
108 0
|
Windows
怎么批量给文件添加拓展名?
怎么批量给文件添加拓展名?
190 0
怎么批量给文件添加拓展名?
【分享】宜搭子表单点击新增自动展开最后一项,折叠前面所有项.
宜搭子表单点击新增自动展开最后一项,折叠前面所有项. by 页一
798 0
|
存储 开发框架 前端开发
ModStartCMS v5.5.0 页面标签支持,用户逻辑优化
ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。
|
C++
新建项无资源字典类型
新建项无资源字典类型
131 0
新建项无资源字典类型
|
前端开发 测试技术 数据库
【测试平台开发】十七、接口编辑页面实现下拉级联选择,绑定接口所属模块
【测试平台开发】十七、接口编辑页面实现下拉级联选择,绑定接口所属模块
|
Rust 自然语言处理 算法
【算法】1389. 按既定顺序创建目标数组(多语言实现)
给你两个整数数组 nums 和 index。你需要按照以下规则创建目标数组: 目标数组 target 最初为空。 按从左到右的顺序依次读取 nums[i] 和 index[i],在 target 数组中的下标 index[i] 处插入值 nums[i] 。 重复上一步,直到在 nums 和 index 中都没有要读取的元素。 请你返回目标数组。 题目保证数字插入位置总是存在。
【算法】1389. 按既定顺序创建目标数组(多语言实现)