浅谈Vue数据响应

2020-06-13 10:23:24易采站长站整理

Vue 中可以用 $watch 实例方法观察一个字段,当该字段的值发生变化时,会执行指定的回调函数(即观察者),实际上和 watch 选项作用相同。如下:


vm.$watch('box', () => {
console.log('box变了')
})
vm.box = 'newValue' // 'box变了'

以上例切入,我想实现一个功能类似的方法 myWatch。

如何知道我观察的属性被修改了?

—— Object.defineProperty 方法

该方法可以为指定对象的指定属性设置 getter-setter 函数对,通过这对 getter-setter 可以捕获到对属性的读取和修改操作。示例如下:


const data = {
box: 1
}
Object.defineProperty(data, 'box', {
set () {
console.log('修改了 box')
},
get () {
console.log('读取了 box')
}
})

console.log(data.box) // '读取了 box'
// undefined
data.box = 2 // '修改了 box'
console.log(data.box) // '读取了 box'
// undefined

如此,便拦截到了对 box 属性的修改和读取操作。

但 res 为 undefined,data.box = 2 的修改操作也无效。

get 与 set 函数功能不健全

故修改如下:


const data = {
box: 1
}
let value = data.box
Object.defineProperty(data, 'box', {
set (newVal) {
if (newVal === value) return
value = newVal
console.log('修改了 box')
},
get () {
console.log('读取了 box')
return value
}
})

console.log(data.box) // '读取了 box'
// 1

data.box = 2 // '修改了 box'
console.log(data.box) // '读取了 box'
// 2

有了这些, myWatch 方法便可实现如下:


const data = {
box: 1
}
function myWatch(key, fn) {
let value = data[key] Object.defineProperty(data, key, {
set (newVal) {
if (newVal === value) return
value = newVal
fn()
},
get () {
return value
}
})
}
myWatch('box', () => {
console.log('box变了')
})

data.box = 2 // 'box变了'

但存在一个问题,不能给同一属性添加多个依赖(观察者):


myWatch('box', () => {
console.log('我是观察者')
})
myWatch('box', () => {
console.log('我是另一个观察者')
})

data.box = 2 // '我是另一个观察者'

后面的依赖(观察者)会将前者覆盖掉。

如何能够添加多个依赖(观察者)?

—— 定义一个数组,作为依赖收集器: