问题背景
- 打开编辑页Form表单时,从后台接口获取已有数据并赋值到对应的输入框中,并通过
v-model
对数据进行绑定,会导致输入框输入无效的状态,且无报错信息
问题分析
- Vue可以检测data中属性property的变化,但是不能直接检测数组、对象的变化
- 直接存在于data中的property可以检测并响应,但是后续定义并赋值property的无法检测
js
// data
return {
form: {}
}
// methods
getForm(res) {
this.form = res.data
}
解决方法
- 通过
forceUpdate
强制更新,但是需要每个输入框都加(不推荐) - 赋值时通过扩展运算符...将源对象拷贝到目标对象(或者
Object.assign
等),表单中的控件会保持响应式,可以进行修改(推荐)
js
this.form = {...res.data}
原理拓展
- 为什么不能直接使用=给对象赋值
- 在 Vue 中使用
v-model
将输入框与form
对象的属性绑定时,Vue 会在内部为这些属性创建 getter 和 setter。这些 getter 和 setter 负责监听属性的变化并通知 Vue 响应式系统。这样,当输入框的值改变时,Vue 将自动更新相关的视图。 - 然而,如果直接给
form
对象赋一个新的对象,Vue 就无法检测到对象的属性变化,因为此时 Vue 只是将新对象赋给了原来的引用,而不是修改了对象的属性。这就导致了输入框无法输入的问题,因为 Vue 没有监听到form
对象的变化。 - 使用
Object.assign()
或扩展运算符...
可以解决这个问题的原因在于它们都是在修改原对象的基础上进行的。虽然它们看起来像是将一个新的对象赋给了form
,但实际上是在原对象的基础上修改了属性。这样做会触发 Vue 的响应式系统,因为 Vue 会检测到属性的变化并更新视图。
- 在 Vue 中使用
- 可以使用深拷贝吗,有必要吗?
- 可以使用深拷贝解决问题
- 但是深拷贝并不是必要的,因为只需要确保更新对象的属性而不是整个对象。深拷贝会创建一个新的对象,这可能会导致性能开销较大,并且在大型对象上可能会引起不必要的复杂性。
- 使用深拷贝虽然可以解决问题,但并不是最佳选择。相反,使用浅拷贝方法如
Object.assign()
或扩展运算符能够更有效地实现我们的目标,而且更符合这种情况的需求。
- JS直接赋值和浅拷贝的区别
- 赋值,将一个对象赋值给一个新的对象的时候,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是一个对象的改变就会改变另外一个对象。
- 浅拷贝,浅拷贝会创建一个对象,再去遍历原始对象,如果原对象的属性值是基础类型,那么就拷贝基础类型,如果是引用类型,则拷贝的是指针。