vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue空值处理

Vue前端空值处理的实战指南与避坑记录

作者:SuperEugene

本文旨在帮助同学们学会在前端真实业务项目里,到底该怎么写空值处理(?.、??、||、if判断、兜底逻辑),以及为什么这么选、会踩哪些高频坑,顺便帮大家拉直JS/TS空值、真值假值的基础概念,助力你们写出规范可维护的团队级代码

帮助同学们学会在前端真实业务项目里,到底该怎么写空值处理(?.、??、||、if判断、兜底逻辑),以及为什么这么选、会踩哪些高频坑,顺便帮你拉直JS/TS空值、真值假值的基础概念,助力写出规范可维护的团队级代码。

很多前端开发者都会遇到一个瓶颈:

代码能跑,但不够规范;功能能实现,但维护起来特别痛苦;一个人写没问题,一到团队协作就各种混乱、踩坑、返工。

想写出干净、优雅、可维护的专业代码,靠的不是天赋,而是体系化的规范 + 真实实战经验

这一系列《前端规范实战》,我会用大白话 + 真实业务场景,不讲玄学、不堆理论,只分享能直接落地的规范、标准与避坑指南。

帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。

引子:为什么要专门聊“空值处理规范”

一句话定位这篇文章:

教你在真实项目里,到底该怎么写空值处理( ?.??||if ** 判断、兜底逻辑),以及为什么这么选、会踩哪些坑**,顺便帮你把 JS/TS 的一些基础概念拉直。

适用人群:

一、先把“空值家族”讲清楚:null、undefined、空字符串、0、false…

日常开发中经常混在一起的几个值:

null           // 明确的“空值”,一般表示“这里有个位置,但现在没有值”
undefined      // 未定义,通常是“压根没传”、“没赋值”
''             // 空字符串
0              // 数字 0
false          // 布尔 false
NaN            // 不是一个合法数字

1.1 “真值/假值”概念(很关键)

在 JS 里,if (xxx) 判断的是“真值/假值(truthy / falsy)”,而不是严格意义上的 true/false。下面这些都是 falsy(假)

其他的基本都被当成 truthy(真)

为什么要先讲这个?

因为 ||&& 这些逻辑运算符,走的就是“真值/假值”逻辑。

比如:

const value = 0;
const result = value || 100;
console.log(result); // 100,而不是 0

0 在 JS 里是假值,所以 value || 100 会拿到 100

这也是我们后面会反复提的一个大坑:“用 ** || ** 做默认值会把合法值 0/''/false 当成没传”

二、可选链?.:安全访问深层属性的标准写法

场景:从后端拿到一个复杂对象,但某一层可能是 null / undefined,直接访问就会炸:

// 假设 user 可能是 null
const city = user.profile.address.city; 
// TypeError: Cannot read properties of null (reading 'profile')

2.1 传统写法 VS 可选链

传统写法(防御式编程):

const city =
  user &&
  user.profile &&
  user.profile.address &&
  user.profile.address.city;

可选链写法:

const city = user?.profile?.address?.city;

2.2 在 Vue 模板里的使用

Vue 2 + Babel 环境Vue 3 默认 Vite 脚手架 一般都支持可选链。

在模板里:

<template>
  <div>
    <p>用户名:{{ user?.profile?.name || '未设置' }}</p>
    <p>城市:{{ user?.profile?.address?.city || '未知城市' }}</p>
  </div>
</template>
<script setup>
const user = ref(null);
// 后端请求完成后,再赋值
</script>

注意:模板表达式里也可以用 ?.||??,和 JS 里一样。

2.3 规范建议:何时必须用可选链?

我在项目里通常建议:

统一规则示例:

三、空值合并运算符??:给“真空”兜底,而不是给所有假值兜底

回顾刚才的例子:

const value = 0;
const result = value || 100;
console.log(result); // 100

如果 0 在业务里是合法值(比如“价格 0 元”、“数量 0 个”),那上面这行其实是错的。

我们想要的是:“只有在值为 null 或 undefined 的时候才给默认值”。

这就是 ?? 的作用。

3.1||vs??对比示例

console.log(0 || 100);       // 100
console.log(0 ?? 100);       // 0

console.log('' || '默认');   // '默认'
console.log('' ?? '默认');   // ''

console.log(null || '默认'); // '默认'
console.log(null ?? '默认'); // '默认'

console.log(undefined || '默认'); // '默认'
console.log(undefined ?? '默认'); // '默认'

总结一句话:

3.2 在真实业务中的推荐用法

典型错误写法(很常见):

// 单价和数量来自接口
const price = item.price || 0;
const count = item.count || 1;
const total = price * count;

在这些场景会出错:

推荐写法:

const price = item.price ?? 0;  // 价格缺失才用 0
const count = item.count ?? 1;  // 只有未传 count 才默认 1

再比如配置项对象

function createDialog(options = {}) {
  const width = options.width ?? 400;         // 未传 width 才采用默认 400
  const closable = options.closable ?? true;  // 未传 closable 才用 true
}

3.3 在 Vue 模板中用??

<template>
  <div>
    <!-- 后端没给 nickName 时显示 '游客',但如果是空字符串就保持空 -->
    <p>昵称:{{ user.nickName ?? '游客' }}</p>
  </div>
</template>

规范建议:

四、兜底逻辑:不仅是运算符,还有“业务上的安全网”

可选链和空值合并属于“语法层面的防御”。

真实项目里,还需要“业务层面的兜底”,比如:

4.1 文本兜底:别让页面渲染出undefined/null

错误示例:

<template>
  <div>
    <!-- 假设 user.name 可能 undefined -->
    <p>用户名:{{ user.name }}</p>
  </div>
</template>

页面可能出现:

<p>用户名:undefined</p>

推荐写法:

<template>
  <div>
    <p>用户名:{{ user?.name ?? '未设置' }}</p>
  </div>
</template>

如果你更谨慎一点,还可以抽成一个小工具函数或指令:

function displayText(value, fallback = '--') {
  if (value === null || value === undefined) return fallback;
  return String(value);
}

模板中:

<p>用户名:{{ displayText(user?.name, '未设置') }}</p>

4.2 数字兜底:0、null、undefined 要区分

常见场景:金额 / 数量 / 积分

<template>
  <div>
    <!-- 如果 amount 为 0,要显示 0 元,而不是 “--” -->
    <p>金额:{{ formatAmount(order?.amount) }}</p>
  </div>
</template>
<script setup>
function formatAmount(value) {
  if (value === null || value === undefined) return '--'; // 真空
  const num = Number(value);
  if (Number.isNaN(num)) return '--';                     // 非法数字
  return num.toFixed(2) + ' 元';
}
</script>

这里的思路是:

4.3 列表兜底:空数组 vs null/undefined

错误写法:

<template>
  <ul>
    <li v-for="item in list" :key="item.id">{{ item.name }}</li>
  </ul>
</template>
<script setup>
const list = ref(null);
</script>

list 为 null 时,Vue 其实不会崩溃,但可读性很差,而且 TypeScript 下会疯狂报错。

推荐规范:

// 假设后端可能返回 { list: null }
interface ApiResponse<T> {
  list: T[] | null;
}
async function fetchUsers(): Promise<User[]> {
  const res: ApiResponse<User> = await request('/api/users');
  return res.list ?? [];
}

Vue 组件里直接:

const users = ref<User[]>([]);
onMounted(async () => {
  users.value = await fetchUsers(); // 一定是数组
});

好处:

五、可读性 vs 防御性:别让“防空代码”毁了代码结构

经常看到这样的代码:

if (user && user.profile && user.profile.address && user.profile.address.city) {
  showCity(user.profile.address.city);
} else {
  showDefaultCity();
}

可读性非常差。我们可以结合 ?. 和业务逻辑重写:

5.1 利用中间变量提高可读性

const city = user?.profile?.address?.city;
if (city) {
  showCity(city);
} else {
  showDefaultCity();
}

如果业务含义更复杂,比如:

city 为空字符串也视为没填

可以:

const rawCity = user?.profile?.address?.city;
const city = rawCity?.trim(); // string 或 undefined
if (!city) {
  showDefaultCity();
} else {
  showCity(city);
}

规范建议:

六、项目中推荐的“空值处理规范(示例版)”

以下是一份可直接落地到团队规范里的示例,你可以根据团队实际情况调整。

6.1 基础规则

6.2 风格对比示例(推荐 vs 不推荐)

不推荐:

// 1. 访问深层属性不做保护
const city = user.profile.address.city;
// 2. 用 || 做默认值
const price = item.price || 0;
const count = item.count || 1;
// 3. 列表用 null 表示“还没加载”
const list = ref(null);

推荐:

// 1. 使用可选链保护
const city = user?.profile?.address?.city;
// 2. 用 ?? 严格处理 null/undefined
const price = item.price ?? 0;
const count = item.count ?? 1;
// 3. 列表统一用 [] 作为初始值
const list = ref([]);

在 Vue 模板中的统一写法示例:

<template>
  <div>
    <p>用户名:{{ user?.name ?? '未设置' }}</p>
    <p>年龄:{{ user?.age ?? '--' }}</p>
    <p>余额:{{ formatAmount(account?.balance) }}</p>
    <ul v-if="orders.length">
      <li v-for="order in orders" :key="order.id">
        订单号:{{ order.id }},金额:{{ formatAmount(order.amount) }}
      </li>
    </ul>
    <p v-else>暂无订单</p>
  </div>
</template>
<script setup>
const user = ref(null);
const account = ref(null);
const orders = ref([]); // 一定是数组
function formatAmount(value) {
  if (value === null || value === undefined) return '--';
  const num = Number(value);
  if (Number.isNaN(num)) return '--';
  return num.toFixed(2) + ' 元';
}
</script>

七、常见踩坑案例拆解

7.1 “把 0 当成没填”——报表类页面的大坑

需求:展示一个指标的环比增长率,后端字段 growthRate,可能是:

错误写法:

<p>环比:{{ growthRate || '--' }}%</p>

growthRate = 0 时,会显示 --%,业务含义严重错误。

正确写法:

<p>环比:{{ growthRate ?? '--' }}{{ growthRate === null || growthRate === undefined ? '' : '%' }}</p>

或者包装一下:

function displayPercent(value) {
  if (value === null || value === undefined) return '--';
  return `${value}%`;
}

模板:

<p>环比:{{ displayPercent(growthRate) }}</p>

7.2 “深层属性访问炸页面”——常见于接口变更

场景:后端有一天把 user.profile 改成 user.info,但你代码里到处是:

user.profile.address.city

迁移时推荐策略:

先统一加可选链防御(短期止血):

const city = user?.profile?.address?.city;

在“数据适配层”做映射,避免在视图层直接跟后端结构硬绑定:

interface UserViewModel {
  city?: string;
  // ...
}
function mapUserDtoToViewModel(dto: any): UserViewModel {
  const profile = dto.profile || dto.info || {};
  return {
    city: profile.address?.city,
    // ...
  };
}

视图层只用 viewModel.city,再配合兜底:

<p>城市:{{ user.city ?? '未知城市' }}</p>

这样即使后端再改结构,你只需要改映射函数,不会到处是 ?. 打补丁。

八、结合 TypeScript:从“到处防空”升级为“类型上减少空值”

如果你的项目已经用 TypeScript,可以进一步 把“空值问题”提前到类型设计阶段解决

8.1 接口类型:把“可选”缩到最小

错误示例(很多后端生成工具会这样):

interface UserDto {
  id?: number;
  name?: string;
  age?: number | null;
  address?: {
    city?: string;
  } | null;
}

视图层到处是:user?.address?.city ?? '未知城市'

更好的做法是:

interface UserViewModel {
  id: number;
  name: string;
  age: number | null;   // 业务上允许为 null
  city: string;         // 至少有兜底
}
function toUserViewModel(dto: UserDto): UserViewModel {
  return {
    id: dto.id ?? 0,                         // 或抛错,看业务
    name: dto.name ?? '未命名用户',
    age: dto.age ?? null,
    city: dto.address?.city ?? '未知城市',
  };

组件里就可以大胆用:

<p>用户名:{{ user.name }}</p>
<p>城市:{{ user.city }}</p>

而不是到处防空。

九、落地建议:如何在现有项目里逐步推行这套规范?

9.1 从“新代码”开始做对

9.2 为高风险页面补一层“空值巡检”

优先排查:

从这些点切入:

9.3 写到团队规范 / README / Contributing 里

可以直接摘抄下面一段到你们项目的规范文档里:

空值处理规范(摘要)

十、总结:把“空值处理”当成一个硬规范,而不是临时脑补

技术成长,从来不是比谁写得快,而是比谁写得稳、规范、可维护

哪怕每次只吃透一条规范,长期下来,差距会非常明显。

以上就是Vue前端空值处理的实战指南与避坑记录的详细内容,更多关于Vue空值处理的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文