今天学习了 Vue 的数据响应式,有点不太好理解,所以写一篇博客整理一下思绪。
要理解 Vue 的数据响应式,首先我们要理解几个概念。
什么是响应式
如果一个物体能对外界的刺激做出反应,那么它就是响应式的。
Vue 的 data 是响应式的
const myData = {
n: 0
}
new Vue({
data: myData,
template: `
<div>{{n}}</div>
`
}).$mount("#app");
setTimeout(()=>{
myData.n += 10
}, 3000)
我们修改了 myData 的 n,我们在界面中看到 n 响应了我,这就是数据响应式。
Vue 2 通过 Object.defineProperty 来实现数据响应式,那么 Object.defineProperty 怎么用呢?
Object.defineProperty
let data0 = {
n: 0
}
// 需求一:用 Object.defineProperty 定义 n
let data1 = {}
Object.defineProperty(data1, 'n', {
value: 0
})
console.log(`需求一:${data1.n}`)
let data2 = {}
data2._n = 0 // _n 用来偷偷存储 n 的值
Object.defineProperty(data2, 'n', {
get(){
return this._n
},
set(value){
if(value < 0) return
this._n = value
}
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)
Object.defineProperty 的用处:
1. 可以给对象添加属性 value
2. 可以给对象添加 getter / setter
3. getter / setter 用于对属性的读写进行监控
我们看看上面的代码,我们在思考,怎么样才能让我们可以监控对 n 的修改,而且不要让外部特意操作而影响到我们,我们可以使用代理。
啥是代理
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) // 括号里是匿名对象,无法访问
function proxy2({data}/* 解构赋值 */){
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
let value = data.n
Object.defineProperty(data, 'n', {
get(){
return value
},
set(newValue){
if(newValue<0)return
value = newValue
}
})
// 就加了上面几句,这几句话会监听 data
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0)return
data.n = value
}
})
return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)
对 myData 的属性读写,全权由另一个对象 vm 负责,那么 vm 就是 myData 的代理。我们不用 myData.n 来操作而是用 vm.n 来操作,这就是代理。
我们看上面的代码,就看到一个特别熟悉的结构。vm = new Vue({data:myData})
,那这会让我们发生什么呢。
vm = new Vue({data:myData})
所以这句话到底会产生什么结果?
1. 会让 vm 称为 myData 的代理
2. 会对 myData 的所有属性进行监控
3. 只要属性变化了,我们就可以进行重新的渲染了
这就是 Vue 的数据响应式的概念。但是其中似乎是存在了一些 bug,比如。
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");
这里的 b 并不会被监听,因为 Vue 不会监听一开始不存在的 obj.b。那我们有什么办法呢?
1. 先把所有的 key 都声明好,然后后面添加属性
2. 使用 Vue.set 或者 this.$set
对象中使用 set 可以新增 key,创建代理和监听,更新 UI。但是数组怎么办呢?
data 中有数组怎么办?
数组的长度是可以一直增加的,数组的 key 就是数组的下标,我们不可能把下标全都声明出来,难道我们要每次都使用 set 吗?
Vue 作者想到了一个办法,就是篡改数组的 API。作者添加了7个变异方法:push()、pop()、shift()、unshift()、splice()、sort()、reverse(),只要使用这些方法就会自动处理对数组的代理和监听并且更新 UI。
扯得稍微有一些远,这大概就是 Vue 的数据响应式的概念。暂时只能理解到这里,如果还有其他的想说的,会在以后的博客中继续说明。