目录
- vue中如何侦测Object变化?
- Object.defineProperty
- 数据劫持原理
- Object 响应式中的问题
- vm.$set内部原理
- vm.$delete内部原理
- vm.$watch内部原理
- 用法
- watch内部实现原理
- deep实现原理
vue中如何侦测Object变化?
js中两种侦测对象变化方法:
- 使用Object.defineProperty
- ES6的proxy(ES6浏览器支持不理想)
Object.defineProperty
该方法定义一个响应式数据,当数据的属性发生变化的时候,通知依赖更新,即向使用到它的地方发送通知
Object可以通过Object.defineProperty将属性转换成getter/setter的形式来追踪变化。读取数据时会触发getter,修改数据时会触发setter
Object.defineProperty(obj, key, {
get() {
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal
}
}
})
这样,修改数据时,我们可以主动通知依赖进行更新,那么问题来了:
-
如何收集依赖?什么时机收集依赖?
思路:
- 任何地方,只要涉及到数据读取,就会触发get函数
- 先收集依赖,然后等属性发生变化的时候,再把之前收集好的依赖循环触发一遍就好了
总结:在getter中收集依赖,在setter中触发依赖
-
依赖收集在哪里?
定义一个Dep类,用来存储当前key的依赖。DEP类主要负责对依赖的收集、删除、发送通知。
-
依赖是谁?
依赖就是用到数据的地方,用到这个数据的地方可能是视图中对应的坑({ {name}}),也可能是开发者写的一个watch,于是抽象一个类Watcher
至此,问题告一段落
- 定义一个响应式数据
//数据劫持,添加依赖追踪
defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
//只有Watcher触发的getter才会收集依赖
Dep.target && dep.depend();
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
// 数据发生变更通知所有的观察者
dep.notify()
}
}
})
//递归树
this.observer(val);
}
- 定义一个Dep类,收集依赖
// Dep发布者相当于vue data 对象中的某一个属性如:name
class Dep {
constructor(vm, key) {
this.subs = [];
}
depend() {
if (Dep.target) {
this.addSub(Dep.target)
}
}
addSub(sub) {
this.subs.push(sub);
}
removeSub(sub) {
remove(this.subs, sub);
}
notify() {
//只负责通知更新,具体是否更新以及更新操作由watcher做
this.subs.forEach(sub => sub.update())
}
}
- 定义依赖 – Watcher类
当new Watcher()实例时,会自动将依赖收集到Dep中
// watcher 相当于视图中对应的坑({
{name}}),一个坑对应有一个观察者,监听此处的数据变化
class Watcher {
constructor(v