一、问题场景:点击“查看”解密收件信息

📄 表格代码示例

<el-table :data="tableData">
  <el-table-column label="收件信息" width="280">
    <template #default="scope">
      <div>
        <!-- 显示收件人姓名和电话 -->
        <span>{{ scope.row.receiverName }} {{ scope.row.receiverPhone }}</span>
        <!-- 查看按钮:解密信息 -->
        <el-button size="small" type="text" @click="handleWatch(scope.row)">查看</el-button>
        <el-button size="small" type="text" @click="handleUpdate(scope.row)">修改</el-button>
      </div>
      <!-- 显示省市区和详细地址 -->
      <div>
        <span>{{ scope.row.receiverProvince }}{{ scope.row.receiverCity }}{{ scope.row.receiverDistrict }}</span>
        <span>{{ scope.row.receiverDetailAddress }}</span>
      </div>
    </template>
  </el-table-column>
</el-table>

❌ 错误写法:直接替换 row

async function handleWatch(row) {
  const res = await getDecryptReceiverInfo(row.id)
  
  // ⚠️ 问题:重新赋值,导致响应式失效
  row = {
    receiverName: res.data.receiverName,
    receiverPhone: res.data.receiverPhone,
    receiverDetailAddress: res.data.receiverAddress,
    ...row
  }
  // 结果:数据变了,但页面没刷新!
}

二、问题根源:Vue 响应式系统无法监听对象替换

🔍 核心原理

Vue 的响应式系统监听的是对象属性的变化,而不是变量引用的替换

  • row = { ... } 是变量重新赋值row 指向了一个新对象。
  • 原始数据(如 tableData 中的项)仍然指向旧对象。
  • Vue 无法感知原始对象被“替换”,因此不会触发视图更新

📌 关键区别

  • ✅ 修改属性:row.name = '张三' → 触发更新
  • ❌ 替换对象:row = { ...row, name: '张三' } → 不触发更新

三、正确方案:使用 Object.assign 修改原对象

✅ 推荐写法

/** 查看解密的收件信息 */
async function handleWatch(row) {
  const res = await getDecryptReceiverInfo(row.id)
  Object.assign(row, res.data)
}

✅ 为什么 Object.assign 有效?

  • row 仍然是同一个对象(引用未变)。
  • Object.assign 是对原对象的属性赋值操作
  • Vue 的响应式系统能监听到这些 set 操作,从而触发视图更新。

四、Vue 3 的优势:无需担心新增属性

Vue 3 使用 Proxy 实现响应式,相比 Vue 2 有巨大提升:

能力 Vue 2 Vue 3
监听属性新增 ❌ 需 this.$set ✅ 自动支持
监听属性删除 ❌ 需 this.$delete ✅ 自动支持
监听数组索引修改 ❌ 需特殊处理 ✅ 自动支持

🎉 这意味着在 Vue 3 中

  • 即使 receiverName 字段之前不存在,Object.assign 添加它也会是响应式的。
  • 无需使用 ref 或 reactive 包装每一行数据,直接修改即可。

五、其他可行方案对比

方法 代码示例 适用场景
Object.assign Object.assign(row, data) ✅ 推荐,批量更新
直接赋值 row.name = 'xxx' 单字段更新
解构赋值(错误) row = { ...row, name: 'xxx' } ❌ 不触发更新
Object.keys + 循环 Object.keys(data).forEach(k => row[k] = data[k]) 复杂逻辑时备用

六、最佳实践总结

✅ 正确做法

  • ✅ 修改对象属性,而不是替换对象。
  • ✅ 使用 Object.assign 批量更新字段。
  • ✅ Vue 3 中无需担心新增属性的响应式问题。

❌ 错误做法

  • ❌ row = { ...row, key: value }
  • ❌ 在 Vue 3 中过度使用 ref 包装每个字段。

七、结语

“不要替换对象,要修改属性” —— 这是 Vue 响应式开发的核心心法。

通过 Object.assign,我们不仅能解决“数据更新但视图不刷新”的常见问题,还能写出更简洁、更易维护的代码。尤其是在 Vue 3 的加持下,响应式系统更加智能和强大,开发者可以更专注于业务逻辑,而不是响应式细节。

下次遇到类似问题,记得先检查:你是不是不小心替换了对象引用?

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐