告别硬编码下拉选项:Vue 3 企业项目字典化改造实录

一个配置页面里七八组下拉选项全是写死的常量,甚至拿中文当业务值传给后端。本文记录一次完整的「字典化改造」实践:从风险盘点、逐项替换、联动过滤到请求合并,附踩坑与收益复盘。

背景:一个"全是硬编码"的配置页

最近在改造某业务系统里的一个配置管理页面。功能本身不复杂:规则列表 + 详情页双 Tab(阈值配置、参数配置),里面散布着设备类型、供应商、覆盖等级、参数类型、运行模式、开关状态等七八组下拉框。

动手前做了一次代码体检,结果触目惊心:

  • 整个目录完全没有使用字典系统 ------useDictionaryStore、loadDictionaries、useOptions、getLabel 一概没有;
  • 所有下拉选项都在 constants.ts 里硬编码;
  • 部分选项甚至直接用中文作为业务值(比如"模式一"直接作为提交值);
  • 而同系统的其他页面早就规范地使用了字典,唯独这个目录是"漏网之鱼"。

这不是代码风格问题,而是实打实的隐患。我们逐一来说。

硬编码的四个坑

1. 中文当业务值,前后端迟早打架

把"模式一"这样的中文直接作为 value 提交给后端,一旦后端迁移、国际化或者接口规范统一,全链路都要跟着遭殃。字典系统的核心价值就是值域与展示分离:接口传编码,页面显文案。

2. 后端数据不兼容

后端如果按字典编码下发给前端(比如 device_type: "C"),而页面选项还是写死的 A、B,就会出现"下拉框里选不到后端返回的值"这种诡异 Bug。

3. 配置不生效

很多企业项目会把字典做成可配置数据(后台可增删选项)。硬编码意味着后台加了新供应商、新参数类型,前端永远看不到------配置形同虚设。

4. 业务扩展困难

每次新增选项都要改代码、走发版,完全丧失了配置驱动的灵活性。

字典化改造:一张表看懂替换方案

改造的第一步,是把硬编码常量与"应使用的字典/公共方案"一一对应起来:

原硬编码选项 替换方案
DEVICE_TYPE_OPTIONS device_type 字典
VENDOR_OPTIONS vendor_name 字典
COVERAGE_LEVEL_OPTIONS coverage_level_full 字典
PARAM_TYPE_OPTIONS param_type 字典
HOUR_OPTIONS hour_of_day 字典(0-23 时)
MODE_TYPE_OPTIONS 复用项目公共的运行模式获取函数
REGION_OPTIONS 复用 useRegionOptions 公共 hook
SWITCH_OPTIONS 复用 SWITCH_STATUS_OPTIONS

几个关键决策点:

能复用公共能力就绝不自造。 区域选项、运行模式、开关状态这些"别人已经封装好"的东西,直接切到公共 hook / 公共常量,既减少维护面,也保证全站行为一致。

选字典要对照页面语义。 覆盖等级这个坑值得单独说:项目里 coverage_level 只有两级,和页面的语义对不上,而 coverage_level_full 才是详细分类------如果只看名字随手选,就会选错。字典名和页面语义对不上时,优先参考同系统其他页面的用法。

值域约定跟着字典走。 开关状态从原来的 'ON'/'OFF' 字符串切换成字典的 0/1 数值后,提交值和后端字段约定一起变了,这属于"字典化"必须连带处理的隐性改动,改完要回头检查类型定义和 Mock 数据是否同步。

联动过滤:设备类型 → 运行模式

字典化只是第一步。改造中发现:运行模式的可选范围取决于当前设备类型,这是典型的级联下拉场景。

核心逻辑用 computed + watch 实现:

ts 复制代码
// 根据当前选中的设备类型,过滤出可选的运行模式
const modeOptions = computed(() => {
  // 设备类型为空 → 返回全量;否则按类型过滤
  return getModeOptionsByDeviceType(queryState.deviceType);
});

// 切换设备类型时,若当前运行模式已不在新列表内,自动清空/落到第一个
watch(() => queryState.deviceType, (val) => {
  const options = modeOptions.value;
  if (!options.some((o) => o.value === queryState.mode)) {
    queryState.mode = options[0]?.value;
  }
});

这里最容易被忽视的是场景差异:

  • 查询栏 :设备类型为空时,运行模式全量展示(用户可以不选设备类型直接查);
  • 新增/编辑表单 :设备类型为空时,运行模式应为空(避免用户提交一个没有归属类型的运行模式)。

同一个"联动"逻辑,在查询和表单里行为完全相反。如果只做一版,就必然有一处是错的。所以改造时要逐场景确认:

  1. 查询栏设备类型为空 → 运行模式全量展示
  2. 表单设备类型为空 → 运行模式无选项、不可勾选
  3. 切换设备类型 → 自动清空失效的运行模式
  4. 用户手动清空运行模式 → 保持为空,不要被 watch 强行填回默认值

另外一个细节:运行模式的选项接口可能是异步加载的,选项数据"迟到"时 watch 要在数据到达后自动补默认值,否则页面一打开就是空的。

请求优化:5 个并发请求合并为 1 个

字典化之后,页面从"写死常量"变成了"按需拉字典"。初期实现是按字典类型逐个请求:

ts 复制代码
// 改造前:5 个字典类型 = 5 个请求
const deviceTypeOptions = useDictionaryOptions('device_type');
const vendorOptions = useDictionaryOptions('vendor_name');
const coverageOptions = useDictionaryOptions('coverage_level_full');
// ...

页面实际用到 5 种字典类型,打开一次就是 5 个并发请求,性能不优雅,也容易被后端盯着看。项目里其实已经存在一个支持批量获取的字典 Store,只是这个页面没用上。改造后:

ts 复制代码
// 改造后:一次批量请求搞定
await dictStore.loadDictionaries([
  'device_type',
  'vendor_name',
  'coverage_level_full',
  'param_type',
  'hour_of_day',
]);

const deviceTypeOptions = dictStore.useOptions('device_type');

收益不只是"5 变 1":

  • Store 内部有 in-flight 合并 (同类型同时被请求时只发一次)和已加载去重(拉过的不再拉),后续其他页面复用成本为零;
  • useOptions 返回的 computed 在模板里自动解包,下拉组件的绑定一行都不用改;
  • 字典加载前后的响应式更新,不影响既有的联动逻辑和默认值 watch;
  • 字典加载和数据列表请求可以并行发起,互不依赖,首屏更快。

这类"顺手优化"在改造中最容易被忽略,但对线上性能是实打实的收益。

质量保障:把 pre-commit 钩子当最后一道防线

改造全程靠三件套兜底:vue-tsc -b 全量类型检查、ESLint、stylelint。

这里有一个真实的翻车案例:某次提交代码时 git commit 直接失败,pre-commit 钩子报错------排查发现是模板里多写了一个闭合标签 ,template 结构不合法。prettier --write 和 lint 修复命令也相继被终止。

这个教训值得写进团队规范:

  1. 钩子报错先看语法,再看 lint------模板结构错误会阻断后续所有自动化;
  2. 类型检查要跑全量 (vue-tsc -b),只盯单文件很容易漏掉跨文件引用;
  3. 改完选项类改动,务必检查类型定义和 Mock 数据 是否同步更新------字典化常伴随值域约定变更(如 'ON'/'OFF' → 0/1),漏一处就是运行时 Bug。

结语

这次改造的最终收益可以总结为三句话:

  • 值域统一:所有下拉选项收口到字典/公共能力,中文当业务值的隐患清零;
  • 配置驱动:后台增删选项前端即时生效,业务扩展不再依赖发版;
  • 体验优化:联动过滤 + 批量请求,交互更合理、首屏请求数从 5 降到 1。

整个过程没有什么高深技巧,但"盘点 → 对照 → 替换 → 联动 → 优化 → 质检"这个流程,几乎是所有企业项目做选项治理的通用范式。如果你手里也有一个"全是硬编码"的老页面,不妨照着这个清单过一遍------收益远比想象中大。

如果你所在的项目也在做类似的字典化或前端治理改造,欢迎在评论区聊聊你们踩过的坑。

相关推荐
明航咨询-陈老师1 小时前
2026信创“硬门槛“解码:国测(Ⅲ级首现/首个Ⅱ级OS) × LS(LS1-4) 双资质实操对照
前端
前端snow1 小时前
别再死磕单 Agent 了!多智能体架构 + LangGraph 实战,一篇全讲透
前端
IMPYLH1 小时前
HTML 的 <tr> 元素
前端·html
行者全栈架构师2 小时前
【鸿蒙心迹】ArkUI 列表性能实战——为什么 200 条数据页面掉到 20fps,LazyForEach 怎么救(HarmonyOS 7.x)
前端·算法·架构
派小心.2 小时前
热力图工具的多页面管理功能怎么比?
前端·数据分析
爱勇宝2 小时前
裁员裁掉了那个干了14年的人:我这才看清职场的5条潜规则
前端·后端·程序员
Hilaku2 小时前
GraphQL 在国内为什么水土不服?
前端·javascript·程序员
Latchh2 小时前
PDF打开不要密码却显示已加密,前端怎么判断
前端·图像处理·人工智能·计算机视觉·pdf
秋天的一阵风2 小时前
🧐 为什么大厂 RAG 从不用纯向量检索?
前端·面试·ai编程