返回博客

用 || 给表单赋初始值,价格是 0 的商品全部空白了

在某后台管理系统的商品编辑功能上线后,QA 反馈了一个诡异问题:价格为 0 的商品,打开编辑弹窗后价格输入框一片空白;更可怕的是,有些商品在「不做任何修改直接保存」之后,所属分类的绑定关系就消失了。

代码看起来人畜无害,甚至有点模板化——直到你盯着它看第三遍。

现象

  • 编辑表单打开时,价格 = 0 的字段显示为空
  • 排序值 = 0 的商品,弹窗里显示的是默认排序值而不是 0
  • 某些状态值为 0(如"停用")的记录,打开弹窗后状态显示为"启用"
  • 不修改任何内容直接保存,部分外键字段(分类 ID、分组 ID)被静默置空

根因

问题出在从接口数据填充表单的这一行代码里:

this.editForm = {
  price:      d.price      || '',
  sort_order: d.sort_order || 0,
  status:     d.status     || 1,
  group_id:   d.group_id   || '',
}

JavaScript 的 ||逻辑或短路运算符,它的语义是:「左侧为 falsy 时,返回右侧」。问题在于 JavaScript 的 falsy 值不只有 nullundefined,还包括:

  • 0(数字零)
  • ""(空字符串)
  • false
  • NaN

所以当接口返回 price: 0 时,0 || '' 直接得到 '',输入框变空。当 status: 0(停用)时,0 || 1 得到 1,状态被篡改成启用。

最危险的是反向提交时的同款问题:

// 提交时
group_id: this.editForm.group_id || null

用户清空了分组下拉框,group_id 变成 '''' || null 得到 null——这次碰巧是对的。但如果 group_id 是数字 00 || null 就把一个合法的分组 ID 清掉了。「碰巧正确」和「设计正确」是两回事。

坑:|| 会把合法的 0 / 空字符串吞掉

|| 的语义是「左侧 falsy 就取右侧」,而不是「左侧是 null/undefined 才取右侧」。在后台管理系统的编辑表单中,0""false 往往都是合法的业务值(零价格、空备注、停用状态),用 || 做默认值会静默覆盖它们,且不报任何错误。PHP 后端返回的 JSON 更容易踩坑,因为 PHP 的弱类型可能把数字 0 序列化成字符串 "0",行为更难预测。

正解

方案一:!= null 三元表达式(兼容 Vue 2 老项目)

this.editForm = {
  price:      d.price      != null ? d.price      : '',
  sort_order: d.sort_order != null ? d.sort_order : 0,
  status:     d.status     != null ? d.status     : 1,
  group_id:   d.group_id   != null ? d.group_id   : '',
}

x != null 是少数推荐使用宽松相等的场景——它等价于 x !== null && x !== undefined,能同时排除这两种「真正没值」的情况,同时让 0""false 安全通过。

方案二:空值合并运算符 ??(Vue CLI 4+ / Vite 项目)

this.editForm = {
  price:      d.price      ?? '',
  sort_order: d.sort_order ?? 0,
  status:     d.status     ?? 1,
  group_id:   d.group_id   ?? '',
}

?? 只在左侧为 nullundefined 时才取右侧,语义上完全正确,代码也更简洁。如果 Babel / 构建工具支持,优先选这个。

提交方向同样要修

// 错:group_id=0 会变成 null
group_id: this.editForm.group_id || null
 
// 对:只有空字符串(用户清空下拉)才转 null
group_id: this.editForm.group_id !== '' ? this.editForm.group_id : null
正解:用 != null 或 ?? 替代 ||

读取 API 数据赋给表单时,一律用 d.field != null ? d.field : defaultValued.field ?? defaultValue,绝不用 d.field || defaultValue。高危字段清单:价格/数量/排序等数值字段、状态/布尔标志位、外键 ID(可能为 0 或空字符串)。

Code Review 检查点

在审查 Vue + Element UI 的表单组件时,重点扫这四个模式:

  • data.field || defaultValue → 替换为 != null 三元或 ??
  • this.form.field || null(提交时)→ 替换为显式 !== '' 判断
  • row.field || 0(模板表达式)→ 确认 0 是否真的应该被替换
  • row.field ? format(row.field) : '-' → 确认 0 和空字符串是否应该显示为 -

一句话外卖

|| 做默认值是给「无值」兜底,但在 JavaScript 里 0、""、false 也是「有值」——只要业务上可能出现这些合法值,就必须改用 ??!= null