Vue的data的BUG
Object.defineProperty的问题
-
Object.defineProperty(obj,'n',{...}) -
这里必须要有一个'n',才能监听&代理obj.n
-
但如果开发者粗心大意,没有给n,或者给了n,但赋值时没有对n进行赋值
import Vue from "vue/dist/vue.js"; Vue.config.productionTip = false; new Vue({ data: { obj: { a: 0 // obj.a 会被 Vue 监听 & 代理,a就是'n' } }, template: ` <div> {{obj.b}} <button @click="setB">set b</button> </div> `, methods: { setB() { this.obj.b = 1; } } }).$mount("#app");这样写,Vue不会给警告,点击setB,页面中会显示 1 吗?
答案是不会,因为Vue不会监听一开始不存在的obj.b
解决办法
- 一开始声明了不就好了(这属实是废话,一开始声明好了,就不会有这个问题了)
- 使用
Vue.set或this.$set
new Vue({ data: { obj: { a: 0 } }, template: ` <div> {{obj.b}} <button @click="setB">set b</button> </div> `, methods: { setB() { Vue.set(this.obj,'b',1) //或 this.$set(this.obj,'b',1) } } }).$mount("#app");这样,点击按钮,页面就会显示1
Vue.set和this.$set的作用
- 新增key
- 自动创建代理和监听(如果没有创建过)
- 触发UI更新(但不会立即更新)
数组的变异
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.$set(this.array,3,'d');
}
}
}).$mount("#app");
这样点击按钮,没有办法添加‘d’,因为数组中index为3的位置,没有办法提前声明
于是乎,尤雨溪想了一招
methods: {
setD() {
this.array.push('d')
}
}
完美解决~
但如果我们打印出这个新数组,会发现这个新数组的原型被修改了
也就是说,这个数组对象,传给Vue之后,Vue会篡改这个数组,它会在中间加一层原型
怎么篡改的?
其实就是加了一层原型链,
以push为例,大致思路如下:
class VueArray extends Array{
push(...args){
const oldLength = this.length//this就是当前数组
super.push(...arg)//super.push 调用我父类上的push
console.log('Vue知道 你push了')
for(let i = oldLength; i<this.length;i++){
vue.set(this,i,this[i])
}
}
}
Vue篡改push,然后对push的变化通过vue.set通知给Vue,那Vue就会知道这个数组变了
总结
对象中新增的key
- Vue没有办法事先监听和代理
- 要使用set来新增key,创建监听和代理,更新UI
- 但最好提前把属性都写出来
数组中新增的key
- 尤雨溪篡改了7个API方便对数组进行增删
- 这7个API会自动处理监听和代理,并更新