数据响应式的中心思想
Vue.js 一个核心思想是数据驱动,视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据。
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data
选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty
把这些 property 全部转为 getter/setter。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
检测变化的注意事项
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。
对于对象
由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data
对象上存在才能让 Vue 将它转换为响应式的。
也就是说,必须有一个’n’,才能监听并代理obj.n(mydata)
Object.defineProperties(obj,'n',{...})
但是如果没有给n怎么办呢?
示例一
Vue会给出一个警告
new Vue({
data: {},
template: `
<div>{{n}}</div>
`
}).$mount("#app");
但是有办法绕过这个警告
示例二
data是一个对象,对象里面有个a,但是在页面中显示的是对象的b,因为Vue只检查第一层,所以不会报错。
new Vue({
data: {
obj: {
a: 0 // obj.a 会被 Vue 监听 & 代理
}
},
template: `
<div>
{{obj.b}}
<button @click="setB">set b</button>
</div>
`,
methods: {
setB() {
this.obj.b = 1; //页面中不会显示 1
}
}
}).$mount("#app");
Vue只监听obj.a,如果a产生变化能反映在视图里,但出现在页面里的是b,如果改变b不会反应在视图里,因为Vue没法监听一开始不存在的obj.b。
解决方法
方法一:开始声明好key,后面再加属性
data: {
obj: {
a: 0 // obj.a 会被 Vue 监听 & 代理
+ b:undefind
}
},
方法二: 使用Vue.set或者this.$set
methods: {
setB() {
Vue.set(this.obj, "b", 1)
//或
this.$set(this.obj, "b", 1)
}
}
- 作用:
- 增加key
- 自动创建代理和监听(如果没有创建过)
- 触发UI更新(不会立刻更新)
对于数组
Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
举个例子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
下面我们来看一个实际示例:
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.array[3] = "d";
}
}
}).$mount("#app");
- 如果我们直接添加d是做不到的,因为这个数组实际上是array:{0:’a’,1:’b’,2:’c’},添加一个d相当于添加一个下标。
- 虽然使用
vm.$set
实例方法可以解决这个问题,但这样我们的所有数组都要用set
,没有办法提前声明。 - 而且因为无法提前预测数组的长度,也不能直接在data里提前声明。
解决方法
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新,见文档变异方法章节。
方法包括
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
总结
对象中新增的key
- Vue没有办法事先监听和代理
- 要使用set来新增key,创建监听和代理,更新UI
- 最好提前把属性都写出来,不要新增key
- 但数组做不到不新增key
数组中新增的key
- 也可以用set来新增key,更新UI(但不能创建监听和代理)
- 不过Vue篡改了7个API方便你对数组进行增删
- 这七个API会自动处理监听和代理,并更新UI
- 结论:数组新增key最好通过7个API