vue watch 侦听器的权衡.md

# vue watch 侦听器的权衡

> 记录实习时一次发生的bug

在 Vue 3 中,`watch` 对数组、对象等‘引用类型’默认只比较**引用地址**:

```js
watch(
() => props.list,
(val) => {
// TODO
}
)
```

对 list 的监听只是地址,如:当父组件用 `splice` 原地修改数组时:

> splice:在当前数组的基础上进行更改
>
> 文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice

```js
// 父组件
list.value.splice(idx, 1) // 原地删除,引用不变
```

子组件的 `watch` **不会触发** ——地址没变,不会触发更改。子组件仍内部保留旧数据,存在潜在的 bug

## 修复

#### 方案一:深层侦听 `{ deep: true }`

```js
watch(
() => props.list,
(val) => {
// TODO
},
{ deep: true } // 深度侦听
)
```

优缺:

  • ✅ 能解决问题
  • ❌ 性能开销:递归遍历每个属性建立依赖,每次嵌套值变化都触发
  • ❌ 语义错位:若只关心增删,但深层侦听会对每个状态的变化进行侦听,如: `item.status` 变化也要触发一次深拷贝,因此若只考虑增删,使用 deep 开销太大

#### 方案二: filter 返回新引用

filter 会返回新的数组,此时地址发生了改变

```js
// 父组件
list.value = list.value.filter((_, i) => i !== idx) // 新数组,新引用
```

- ✅ 地址变了 →watch 自然触发

若只是对增删进行处理,建议将 `splice` 替换为`filter` ,只用一行代码,避免了 `deep: true` 带来的所有副作用

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论