响应式是vue独特的特性之一,在修改数据后,视图会自动更新
其中的原理是在我们将一个JavaScript对象作为参数传入到Vue中时,Vue会遍历该对象的所有属性,使用 Object.defineProperty 把这些属性全部转为 getter/setter,并且会返回一个组件实例作为该对象的代理。
当修改组件实例的数据时,就会同时修改传入的JavaScript对象的数据,而且vue同时会监听这个JavaScript对象,在JavaScript对象的数据发生修改时,同时也会更新组件实例的数据
每个组件实例都对应一个watcher 实例,watcher 实例会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染
正常情况下vue只会监听实例化之前传进来的数据,若在实例化后增加新属性,vue不会将新属性转化为getter/setter,因为没有setter,所以在新属性的数据发生改变时,就不会通知watcher重新渲染视图
所以可以在传入的参数中预留会用到的属性,这样就可以对属性进行监听
或者是调用vue提供的方法来添加新属性
对象
Vue.set(object, propertyName, value)
或者
vm.$set(object, propertyName, value)
如果要添加多个属性,使用Object.assign() 或 _.extend()也不会让新属性被监听,在这种情况下,你应该用原对象与要混合进去的对象的属性一起创建一个新的对象
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
数组
vm.$set(vm.items, indexOfItem, newValue)
或者
Vue.set(vm.items, indexOfItem, newValue)
或者
vm.items.splice(indexOfItem, 1, newValue)
但是在某种情况下,即使新属性没有被监听,也会在属性被修改后更新到视图上
<div id="app">
<span class=span-a>
{{obj.a}}
</span>
<span class=span-b>
{{obj.b}}
</span>
</div>
var app = new Vue({
el: '#app',
data: {
obj: {
a: 'a',
}
},
})
app.obj.a = 'a2'
app.obj.b = 'b'
上面的代码中app.obj.b的值会被显示在视图上,因为a属性被修改后,通知watch更新视图, 在更新视图的时候,Vue 会去做 diff,发现b的数据也发生了变化,所以也会把b的数据更新到视图上
这里也可以看出vue更新视图是异步的,如果是同步的话上面的代码中,b就不会更新在视图上,只有等到a再次被修改后才可以
vue文档的解释
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果同一个 watcher 被多次触发,只会被推入到队列中一次。
这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。
然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作