vue2:Object.defineProperty
最主要的两个属性,get和set
get
属性的getter属性,当模板使用变量时,就会触发这个函数,执行时不传入任何参数,此函数一般会返回自身的值
set
属性的setter函数,当属性值被改变时,会触发这个函数,执行时传入一个参数,也就是被赋予的新值,此函数一般会去间接的将最新的值更新给视图
实现响应式
defineReactive(data, key, value) {
// 一个 key 属性对应一个 Dep 对象
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
set(newValue) {
// 如果值未变,就不操作
if (newValue === value) {
return
}
// 更改值
value = newValue
// 通知 watcher 更新值====== notify 内部调用 watcher 的 update 方法更新视图
dep.notify()
},
get() {
// 如果是视图层使用的数据触发 get 函数,就把对应的 watcher 添加到 dep 的 subs 中
if (Dep.target) {
dep.addSub(Dep.target)
}
return value
},
})
}
Dep
class Dep {
constructor() {
this.subs = []
}
// 添加 watcher 到 subs 中
addSub(sub) {
this.subs.push(sub)
}
// 调用 notify 函数通知 watcher =======通过 update 函数的调用通知
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
let dep = new Dep()
Watcher
class Watcher {
constructor(node, name, vm) {
this.node = node
this.name = name
this.vm = vm
//
Dep.target = this
this.update()
this.target = null
}
update() {
this.node.nodeValue = this.vm[this.name]
}
}
缺点
- 检测不到对象属性的添加和删除
- 数组API方法无法监听到
- 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题
可以用$set方法解决添加失去响应式问题
正因为defineProperty自身的缺陷,导致Vue2
在实现响应式过程需要实现其他的方法辅助(如重写数组方法、增加额外set、delete方法)
vue3:Proxy
它监听是针对一整个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性了
function reactive(obj) {
if (typeof obj !== 'object' && obj != null) {
return obj
}
// Proxy相当于在对象外层加拦截
const observed = new Proxy(obj, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
return res
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
return res
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key)
return res
}
})
return observed
}
Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等,这是Object.defineProperty不具备的
Proxy不兼容IE,defineProperty能支持到IE9