2026年了,接口库存返回 0,页面却显示「--」,是前端把 0 当成没值了吗?
接口返回 0,页面却显示「--」,多数不是接口没返数据,也不是字段名拼错,而是前端在取值那一步用了 || 或 if (!x) 这类真值判断,把 0、空字符串、false 一起当成了「没值」。?? 只对 null 和 undefined 生效,能保住 0,但兜不住 NaN,也不能和 || 或逻辑与不加括号混写。按 2026 年常见交付习惯,判断「无值」和「零值」要分两层处理,不能一个 || 走天下。
0 被吃掉,通常落在哪几种写法
联调现场里,接口 JSON 里字段确实是 0,Network 面板能看到,页面上却是横杠。往代码里翻,问题基本落在取值那一行。JavaScript 的假值包括 0、-0、0n、空字符串、false、null、undefined、NaN;用真值判断兜底,这几类会被一锅端,而业务上真正需要兜底的往往只有 null 和 undefined。
- 逻辑或兜底:count || '--',库存 0 变横杠,看不出是零库存还是没数据。
- 取反判断:if (!amount) return '--',金额为 0 时整块显示为空。
- 三目简写:value ? value : '-',效果和 || 一样,0 被吞。
- 解构默认值:const { count = '--' } = data,默认值只在 undefined 时生效,null 来了兜不住。
这些写法的共同点是判定条件比业务语义宽。合格口径是:只有业务上明确定义为「没有」的值才兜底,其余值原样展示。排查时先拿 0、空字符串、null 三组数据各跑一遍列表、详情、导出。
空值判定分四层,先定语义再写代码
0 被吃掉很少是单点写错,而是数据、取值、展示、兜底四层都在做宽容判断,最后没人说得清值在哪一层丢的。分层后每层只负责一件事,出问题可以一层层往里收。
- 数据层:和后端确认字段语义——null 表示没有数据,0 表示有数据且为零,两者不能共用一个兜底;字段类型是数字还是字符串也要定死。
- 取值层:只在可能缺失的字段上用 ?? 和 ?.;可选链只用于链式对象可能断开的场景,不要整条链路都加,否则真正的数据缺失会被静默吞掉。
- 展示层:统一走格式化函数,把「无值」映射成横杠,把 0 原样渲染成 0,金额类再单独处理精度和千分位。
- 兜底层:对确实不该出现 undefined 的字段加断言或上报,让静默空值暴露在日志里。
取值层做的是「不抛错」,展示层做的是「显示对」,这两件事混在同一个表达式里更容易返工。常见做法是取值层只管用 ?? 和 ?. 把链路走通,是否展示占位符交给展示层决定。
展示层收敛:一个格式化函数管住列表、详情、导出
把空值规则收进一个格式化函数,比每个组件各写一遍兜底省事。合格标准是:同一字段在列表、详情、导出三处显示结果一致;0、空字符串、null 各有确定展示;数量和金额规则分开。按 2026 年常见做法,这套函数放在 utils 目录并配一份用例,改一处全站生效。
这里有个顺序坑:Number('') 和 Number(null) 的结果都是 0,先转数字再判空,空字符串和 null 会先变成 0,然后被当成「零库存」展示出去。正确顺序是先判 null 和 undefined,再用 Number.isFinite 判有效数字,最后做数值格式化。
- 数量类:先判 v == null,再判 Number.isFinite(Number(v)),都不通过才显示横杠。
- 金额类:先转数字再按最小单位处理,避免浮点误差,展示阶段统一加千分位。
- 文本类:空字符串按业务单独定,多数场景原样展示或给占位提示,不要和 null 混为一谈。
|| 和 ?? 怎么取舍,别全站机械替换
两者不是新旧替代关系,语义本来就不同:|| 取第一个真值,?? 取第一个有值。选错语义,0 就丢了。不建议全站把 || 机械替换成 ??,那是另一种返工。
- 兜底范围:|| 兜掉所有假值,包括 0、空字符串、false;?? 只兜 null 和 undefined。
- 能否保住 0:|| 保不住,?? 能保住。
- 能否兜住 NaN:两者都兜不住,NaN 不属于 null/undefined,需要用 Number.isFinite 单独判。
- 语法限制:?? 不能与 ||、逻辑与直接混用,必须加括号,否则解析报错。
- 改动风险:全量替换风险偏高,原本靠空字符串兜底的逻辑会被改坏,尤其是表单回填和搜索条件拼装。
三种收敛方案的经验区间对比
- 单页点状修:只改当前页兜底,常见 0.5~2 小时/页;适合临时活动页,但列表、详情、导出容易各写一套。
- 全站替换 || 为 ??:改动面大,回归成本常见 1~3 人日,且会改坏依赖空字符串兜底的旧逻辑。
- 空值字典 + 统一格式化函数:首次梳理常见 0.5~1 人日,页面越多越划算;后续新页面复用同一口径。
取舍倾向是:新增代码默认用 ?? 表达「缺省」,只有确实想兜住空字符串或 false 的场景才用 ||,并写一句注释说明意图。下一个接手的人不用猜这行代码是有意还是手滑。
交付现场:0 被当成缺货的那次返工
一类常见的后台项目返工是这样的:约束条件是后端字段文档只写了「库存,number」,没说明 0 和 null 的业务语义,前端按以往习惯用 || 兜了横杠,联调时也没有零库存样例。做法上,列表页先上线,详情和导出随后各写一遍兜底,三处口径不一致。结果是上线后运营看到库存 0 的商品显示横杠,误判为缺货,手动下架了几条在售商品;后来把展示层收编成统一格式化函数,按四层法重过列表、详情、导出,返工落在 1~3 人日的经验区间。
适用与不适用边界
适合按这套分层处理的场景:后台管理系统、订单与库存与金额类展示、H5 和小程序共用的同一份数据口径、表单回填、需要对外导出的报表页。这些场景里字段为 0 通常有业务含义,丢一次就可能引起误判。
不必上纲上线的情况:纯静态展示页、字段本身不可能为 0 的文本,为它们单独写格式化函数只会扩大维护面;一次性活动的临时页面,如果没有金额和数量字段,用最简单的兜底即可。
可独立摘录的边界判断是:只有当一个字段的 0 在业务上确实有意义时,才值得为它单独设计空值口径;否则统一兜底反而更省维护成本。
常见问题
0 ?? '--' 的结果到底是哪个?
结果是 0。?? 只在左侧为 null 或 undefined 时才取右侧,0 属于有值,会被原样保留。
?? 和 || 写在一起为什么直接报错?
规范要求混用时必须加括号,否则解析阶段就判为语法错误。常见写法是给 ?? 部分单独加括号,写清优先级。
解构默认值能兜住 null 吗?
兜不住。解构默认值和函数默认参数一样,只在值为 undefined 时生效,null 会原样赋进去,需要在取值层再用 ?? 处理一次。
NaN 用 ?? 兜不住,该怎么判断?
用 Number.isFinite 或 Number.isNaN 单独判。NaN 不属于 null/undefined,?? 会把它当有效值放过去,页面上就会出现 NaN。
接口返回字符串 '0',页面反而正常,为什么?
因为非空字符串 '0' 是真值,|| 不会替换它。同一字段在数字和字符串两种类型下表现不同,先统一类型再谈兜底。
-
2026年了,列表页用 keep-alive 保住筛选和滚动位置,从详情返回数据却还是旧的,返回时到底该不该每次重拉?
日期:2026年10月22日 阅读:91
-
React 列表页一滚动,接口就一串串地发,是 useEffect 依赖里那个对象每次都换新吗?
日期:2026年10月21日 阅读:95
-
2026年了,弹窗的灰色遮罩只盖住卡片那一块,周围页面还能点,是遮罩没铺满吗?
日期:2026年10月19日 阅读:96
-
AI 生成的弹窗和表单,键盘一按 Tab 就跑偏,2026 年上线前还值得花时间补焦点吗?
日期:2026年10月18日 阅读:98
-
2026年了,小团队只我一个前端,想往体验架构师走,先补设计审美还是先补性能和SEO?
日期:2026年10月17日 阅读:37




