在 Vue2.x 中,如果你在组件的 data
里这样写:
data() {
return {
form: {}
}
},
watch: {
'form.a'(newVal, oldVal) {
// 监听 form.a 的变化
console.log('form.a changed:', oldVal, '→', newVal)
}
}
然后在某个业务流程里执行了
this.form = {}
会带来以下几个影响:
1. 触发一次「从旧对象到新对象」的 watcher
当你把 this.form
整个替换成一个新的空对象时,Vue 会:
- 先 卸载 对原来那个
form
对象上所有依赖于form.a
的响应式追踪; - 再 重新执行 表达式
form.a
,把它看作一个新的依赖。
因为旧的 form.a
(可能是 undefined
、null
、或者你之前赋过的某个值)被替换成新对象上的 undefined
,所以 watcher 会立刻以
oldVal = (原来 form.a),newVal = undefined
的形式触发一次。
2. 新对象上并没有 “a” 这个响应式属性
新赋值的 form = {}
本身是响应式的——Vue 会把这个新的对象走一遍 Observer—but 它目前没有任何名为 a
的属性:
console.log(this.form.a) // => undefined
- 由于 JS 的限制,对象上新加的属性默认 不是响应式(也就不会被 Vue 的依赖收集追踪到)。
- 也就是说,如果之后你写
this.form.a = 123
,是不会 再触发watch: { 'form.a': … }
的。
3. 如何让新对象上的 a
继续被监听?
如果你的业务确实需要动态给 form.a
赋值,并且希望继续触发 watcher,有两种常见做法:
-
在 data 里提前声明
data() { return { form: { a: null // 提前声明好 a,保证从一开始就是响应式的 } } }
-
使用
Vue.set
/this.$set
// 替换对象后,给新对象动态添加 a this.$set(this.form, 'a', 123) // 这样 Vue 就会为新加的属性走一遍响应式系统,watcher 会被触发
小结
-
替换整个对象(
this.form = {}
)时,Vue 会先卸载旧的依赖,再重新“评估”form.a
,因此会触发一次从旧值到undefined
的 watcher。 - 新对象没有预先声明的
a
属性,之后直接this.form.a = …
不会被 Vue 监听到。 - 若要继续监听新对象的
a
,要么初始化时就写好,要么用Vue.set(this.form, 'a', value)
来添加。
另外
在 Vue2.x 中,如果你在 data
里这样声明:
data() {
return {
form: { a: null }
}
},
watch: {
'form.a'(newVal, oldVal) {
console.log('form.a changed:', oldVal, '→', newVal)
}
}
然后在业务流程里直接执行:
this.form = {}
会带来两个主要影响:
1. 会触发一次从 null
→ undefined
的 watcher
当你把整个
form
对象替换成新对象时,Vue 会把旧对象上的所有响应式依赖卸载掉,
然后在新对象上重新建立依赖。-
因为旧对象上的
form.a
是null
,新对象上没有a
属性,取值时会得到undefined
,
所以 watcher 会立刻以form.a changed: null → undefined
的形式被触发一次。
2. 新对象上的 a
属性不再是响应式的
-
新的
{}
里没有预先声明a
,此时如果你再写this.form.a = 123
这会在对象上动态添加一个普通属性,不会走 Vue 的响应式系统,
因此 不会 触发你对form.a
的 watcher。
如何在“重置”或“清空”form 时不破坏 form.a
的响应式?
方案 1:不要替换整个对象,只修改它的内容
-
使用
Object.assign
回填默认值// 定义一个默认模板 const defaults = { a: null, b: '', c: [] } // 把 defaults 的所有属性复制到 this.form 上 Object.assign(this.form, defaults) // 此时 this.form 仍是同一个对象,其上已声明的属性依旧响应式
-
手动删除 or 重置无用字段
// 删除所有不需要的字段 Object.keys(this.form).forEach(key => { if (key !== 'a') { this.$delete(this.form, key) } }) // 明确重置 a this.form.a = null
这样 Vue 会一直监听同一个 form
对象,既不会触发 “null → undefined” 那次多余的 watcher,也能保持后续 this.form.a = xxx
的响应式。
方案 2:替换后用 Vue.set
(或 $set
)重新声明属性
如果你确实需要把 form
换成一个全新的对象,也可以这样做:
// 故意替换成空对象
this.form = {}
// 用 $set 重新添加 a,确保它是响应式的
this.$set(this.form, 'a', null)
// —— 此后 this.form.a = 123 会触发 watcher
注意:Vue2.x 里新增属性必须走
$set
才能变成响应式。
方案 3:深度监听整个 form
如果你的表单字段非常多、动态性强,也可以把 watcher 改为深度监听完整表单:
watch: {
form: {
handler(newForm, oldForm) {
console.log('form 对象发生变化了:', newForm)
},
deep: true
}
}
- 优点:任何新增、删除、修改都会触发一次回调。
- 缺点:回调里只拿到完整对象,不能像
form.a
那样精准区分是谁变了,需要自己做判断。
小结
-
替换整个
form
会产生一次null → undefined
的 watcher,并且之后新增的a
不再响应式。 -
推荐做法:不替换根对象,而是用
Object.assign
、手动增删字段、或Vue.set
等方式来 更新内容,保持同一个对象的响应式链路。 - 必须新增属性时,记得走
Vue.set(this.form, 'key', value)
,否则 watcher 和绑定都不会生效。