什么是Vue数据响应式
一个物体能对外界的刺激做出反应,它就是响应式的
- 我打你一拳,你会喊疼,那么你就是响应式的
- 当我修改Vue实例中的数据时,视图就会重新渲染,出现最新的内容。这就是Vue的数据响应式 例:
const vm = new Vue({
components: { Demo },
data: {
n: 0
},
template: `
<div>
{{n}}
<button @click="add">+1</button>
</div>
`,
methods: {
add() {
this.n += 1
}
}
}
).$mount('#run')
数字n在页面上显示的是0,当我按下+1按钮时,数字+1,这就是数据响应式
响应式原理
Vue通过Object.defineProperty函数让我们能够精确地添加或修改对象地属性,getter和setter来读和写内容
Object.defineProperty的用法:
let data1 = {}
Object.defineProperty(data1, 'n', {
value: 0
})
//用 Object.defineProperty 定义 n
getter和setter的用法:
let obj = {
a: 1,
get b(){
return this.a+1;
},
set c(value){
this.a += value
}
}
console.log(obj.a) // 1,当前obj 中 a 的值
console.log(obj.b) // 2,不需要加括号直接调用 b 方法
obj.c = 10
console.log(obj.a) // 11,通过 c 方法设置 a 的值
console.log(obj.b) // 12
当Object.defineProperty和getter、setter组合起来使用,我们就可以实现一个对象控制另一个对象的读写,也就是代理
let data1 = proxy({ data:{n:0} }) // 括号里是匿名对象,无法访问
function proxy({data}){
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0)return
data.n = value
}
})
return obj // obj 就是代理,data1就是obj
}
从上面的代码,对比Vue,看出什么相似之处了吗?
let data1 = proxy({ data:{n:0} }) ≈ vm=new Vue({data:mydata})
现在我们再来看Vue到底做了什么,是不是就有了一点头绪呢?
vm=new Vue({data:mydata})
- Vue会让vm成为mydata的代理
- 会对mydata所有属性进行监控
- 当数据改变时触发UI更新
Vue的data的bug
我们使用Object.defineProperty(obj,'n',{...})的时候,必须要有一个'n',才能对其进行监听和代理,而且Vue为了节约计算能力,只会检查第一层属性,如果n在第二层,是没有警告的。如果忘记设这个'n'怎么办?
比如这样:
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; //这是不会有任何警告或提示,但是obj.b = 1并不会生效
}
}
}).$mount("#app");
解决方法:
使用Vue.set或this.$set(这俩是一模一样的,随便用哪个都行)
还是刚才的代码,这次我们使用Vue.set
new Vue({
data: {
obj: {
a: 0 // obj.a 会被 Vue 监听 & 代理
}
},
template: `
<div>
{{obj.b}}
<button @click="setB">set b</button>
</div>
`,
methods: {
setB() {
Vue.set(this.obj,'b',1)//这次就跑通了
}
}
}).$mount("#app");
Vue.set做了什么?
- 新增key
- 自动创建代理和监听(如果没有创建过的话)
- 触发UI更新
数组的变异方法
- 数组没有办法提前声明所有的key,因为数组的key是下标,而数组的长度是可以一直增加的,比如这样,点击按钮后并不会添加d到数组,因为数组内并没有下标为3的key
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.array[3] = "d"; //点击按钮后,页面中并不会添加d
}
}
}).$mount("#app");
解决方法:
尤雨溪给我们提供了便利的做法,直接使用push
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.array.push('d')//这样就将'd'push到了数组array中
}
}
}).$mount("#app");
尤雨溪提供的这个push方法是被修改过的,它包含了额外的一层原型,这层原型共有七个API:push(新增)、pop(弹出最后一个)、shift(弹出第一个)、unshift(在第一个新增)、splice(从中间删除某一项)、sort(正序)、reverse(倒序),而这层原型之后才指向真正的push