[Vue] v-model 绑定对象不实时更新

[Vue] v-model 绑定对象不实时更新

原文链接: 做棵大树

在最近参与的一个项目中,前端用到了 vue.js 框架,期间有个功能需要动态的向一个被绑定的对象中添加属性。但是在实际应用中问题出现了:在向对象中添加属性后,与对象绑定的组件内容却未发生变化,必须要再次刷新组件,其内容才会变为更改后的内容。

起初我以为是属性没有添加成功,因为在我的印象中 v-model 是双向绑定的,不会出现不更新的状态。在我查看 Devtools 中的监控后,发现对应的对象确实添加了指定的属性。

于是,我前去查看了官方文档,找到了官方给出的解释:Vue.js如何追踪变化

官方解释

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

[Vue] v-model 绑定对象不实时更新_第1张图片

官方解释图例

检测变化的注意事项

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

对于对象

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:

var vm = new Vue({  
  data:{  
    a:1  
  }  
})  
  
// `vm.a` 是响应式的  
  
vm.b = 2  
// `vm.b` 是非响应式的  

对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。例如,对于:

Vue.set(vm.someObject, 'b', 2)

您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

`this.$set(this.someObject,'b',2)
`

有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign()_.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`  
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })  

这是对于对象赋值的解决方式,在采用了官方的解决方案 this.$set(object, key, value) 后确实实现了实时更新的效果。同时对于数组等情况,可查看 余下官方文档

为什么会这样呢?

如官方所说 “由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。” ,但是为什么会这样呢?

借用 Segmentfault UKer 的回答:

ECMAScript中有两种属性: 数据属性访问器属性; 数据属性的描述符为:Configurable,Enumerable,Writable,Value; 访问器属性的描述符为:Configurable, Enumerable,set,get。

当我们使用new Vue(obj),其内部发生了大体如下代码的转换,即,将数据属性转换为了访问器属性

function Vue(obj){  
    obj.data.keys().forEach((prop, index) => {  
      Object.defineProperty(obj.data, prop, {  
        set(){  
          //可以在此处进行事件监听  
        },  
        get(){  
      
        }  
      })  
    })  
    return obj;  
 }  

但是当我们后面再次使用普通的赋值,仅仅是赋值了一个数据属性的,这个属性是不会具有访问器属性的事件监听功能的。

至此,v-model 绑定数据不实时更新的问题方才得到了解决。


你可能感兴趣的:(vue.js,javascript)