如果一个物体能对外界的刺激作出反应,它就是响应式的。
Vue的data是响应式
const vm=new Vue({data:{n:0}})
如果我修改了vm.n,那么UI中的n就会来响应我
要理解Vue数据响应式,我们需要对以下概念有一个了解
- getter
- setter
- Object.defineProperty
getter/setter用于对属性的读写进行监控,Object.defineProperty可以给对象添加属性value,和getter/setter
Vue作者尤雨溪为了不让用户随便篡改data,就会监听data的变化,再用一个新的对象作为代理(一种设计模式)
let myData={n:0}
let data5=proxy({data:myData}) //括号里是匿名对象 无法访问
function proxy({data}){ //析构赋值
let value=data.n //用新的value获取data.n,这样就可以监听data.n的变化
Object.defineProperty(data,'n', { //这里的n是虚拟,不能被修改
get(){
return value
},
set(newValue) {
if(newValue <0 ) return
value=newValue
}
})
const obj={} //声明一个新的对象,把data.n全权负责给obj,这样不论怎么篡改,都不会影响
Object.defineProperty (obj,'n' ,{
get () {
return data.n
},
set(value) {
if(value<0) return
data.n=value
}
})
return obj //obj就是代理
}
data里的属性可以有多个,用一个for循环把key找出来,然后一一加代理
Object.defineProperty的问题
Object.definePropety(obj, 'n', { ... } )
必须要有一个'n',才能监听&代理obj.n,如果n没有定义,Vue会给出一个警告。但是Vue只会检查第一层的属性,但是如果代码写成这样,那么Vue就不会给警告
new Vue ( {
data: {
obj: {
a:0
}
},
template: `
<div>
{{obj.b}}
<button @click="setB"> set b </button>
</div>
` ,
methods : {
setB() {
this.obj.b=1
}
}
}).$mount("#app")
页面上不会显示obj.b,因为它没有定义。但是Vue不会报错,我们成功绕过了Vue第一层的见检查。
一句到底就是,如果要代理&监听对象的key,这个key必须存在
解决方法:①把key都声明好,后面不再加属性②使用Vue.set或者this.$set
Vue.set和this.$set的作用是:
- 新增key
- 自动创建代理和监听(如果没有创建过)
- 出发UI更新(异步更新,这里不细讲)
举例
this.$set(this.obj, 'm' , 100)
但是有些特殊情况没法提前声明所有的key
data中有数组
数组的长度可以一直增加,下标就是key。如果每次都用Vue.set或者this.$set会很麻烦
Vue文档中介绍的,7种Vue新增的编译方法
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
// this.array[3] = "d"; //请问,页面中会显示 'd' 吗?
this.array.push("d"); //能正确显示
console.log(this.array);
}
}
}).$mount("#app");
Array第一层原型里有这七个方法
总结
简而言之,数据响应式就是 Vue 对 data 进行了内部的监听和代理,从而使数据不被其他手段改变,在正确方法中实现的 data 改变并实时渲染到页面 UI 中