发布者订阅者模式
vue的响应式系统是由发布者订阅者模式来收集依赖和派发更新的,所以首先实现Dep的类
class Dep {
constructor() {
//订阅
this.subscribe = new Set()
}
//添加订阅
// addEffect(effect) {
// this.subscribe.add(effect)
// }
//添加订阅
depend() {
if (activeEffect) {
this.subscribe.add(activeEffect)
}
}
//通知更新
notify() {
this.subscribe.forEach(effect => {
effect()
})
}
}
测试数据
const info = reactive({ counter: 10, name: "jack" })
const foo = reactive({ height: 18 })
watchEffect(function () {
console.log("effect1:", info.counter * 2)
})
watchEffect(function () {
console.log("effect2", info.counter * info.counter)
})
watchEffect(function () {
console.log("effect3", foo.height)
})
info.counter++
foo.height = 16
实现watchEffect函数
const deep = new Dep()
let activeEffect = null
function watchEffect(effect) {
activeEffect = effect
effect()
//deep.depend()
activeEffect = null
}
实现defineProperty
//数据响应式函数
//将原始数据传入
function reactive(raw) {
Object.keys(raw).forEach(key => {
const dep = getDep(raw, key)
let value = raw[key]
//利用defineProperty对函数的value值进行处理
Object.defineProperty(raw, key, {
//访问数据
get() {
//访问数据时进行收集依赖
dep.depend()
return value
},
//更改数据时进行通知
set(newValue) {
value = newValue
dep.notify()
}
})
})
return raw
}
这里需要对dep进行一个处理,一个dep应该收集一个属性,每一个属性对应不同的dep,当数据进行更新时,派发相应的dep对象进而更新相对应的数据
// Map({key: value}): key是一个字符串
// WeakMap({key(对象): value}): key是一个对象, 弱引用
const targetMap = new WeakMap();
function getDep(target, key) {
// 1.根据对象(target)取出对应的Map对象
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
// 2.取出具体的dep对象
let dep = depsMap.get(key);
if (!dep) {
dep = new Dep();
depsMap.set(key, dep);
}
return dep;
}
测试结果
definePropertyProxy实现
function reactive(raw) {
return new Proxy(raw, {
//target==传入的raw
get(target, key) {
//dep不能在get外面定义,因为key在get外面没有,所以需要定义两次
const dep = getDep(target, key)
dep.depend()
return target[key]
},
set(target, key, newValue) {
const dep = getDep(target, key)
target[key] = newValue
dep.notify()
}
})
}
其他的东西不变,用Proxy代替defineProperty,测试结果是一致的
Proxy
使用Proxy的优势
- Object.definedProperty 是劫持对象的属性时,如果新增元素,那么Vue2需要再次 调用definedProperty,而 Proxy 劫持的是整个对象,不需要做特殊处理
- 修改对象的不同
- 使用 defineProperty 时,我们修改原来的 obj 对象就可以触发拦截
- 而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截
- Proxy 能观察的类型比 defineProperty 更丰富
- has:in操作符的捕获器
- deleteProperty:delete 操作符的捕捉器
缺点
- Proxy 不兼容IE,也没有 polyfill, defineProperty 能支持到IE9
网友评论