Vue2
数据劫持(Object.defineProperty)+发布订阅模式
1.Vue在初始化的时候需要通过Object.defineProperty 将data里的属性变成具有getter、setter可响应的
2.Vue 在$mount中的会实例化一个 Watcher 实例,实例化过程中会触发 get() 将Watcher实例指向 Dep.target;
3.在渲染过程中使用data中的属性会触发该属性的 getter,进而会触发 dep.depend() 进行依赖收集;闭包的dep实例的subs添加观察它的 Watcher 实例;Watcher 的deps中添加dep;
4.当data的某个属性值变化后,会触发该属性的setter,dep.notify() 派发更新;触发dep.subs中Watcher实例执行 update() 方法,最后实际上是调用Watcher的回调函数cb,进而更新视图。
Vue3
// 存储副作用函数的桶
const bucket = new WeakMap()
// 响应式
function reactive(obj){
return new Proxy(obj, {
// 拦截读取操作
get(target, key) {
// 将副作用函数 activeEffect 添加到存储副作用函数的桶中
track(target, key)
// 返回属性值
return Reflect.get(target,key)
},
// 拦截设置操作
set(target, key, newVal) {
// 设置属性值
Reflect.set(target,key,newVal)
// 把副作用函数从桶里取出并执行
trigger(target, key)
}
})
}
// 收集依赖
function track(target, key) {
let depsMap = bucket.get(target)
if (!depsMap) {
bucket.set(target, (depsMap = new Map()))
}
let deps = depsMap.get(key)
if (!deps) {
depsMap.set(key, (deps = new Set()))
}
deps.add(activeEffect)
}
// 派发更新
function trigger(target, key) {
const depsMap = bucket.get(target)
if (!depsMap) return
const effects = depsMap.get(key)
effects && effects.forEach(fn => fn())
}
// 用一个全局变量存储当前激活的 effect 函数
let activeEffect
function effect(fn) {
// 当调用 effect 注册副作用函数时,将副作用函数复制给 activeEffect
activeEffect = fn
// 执行副作用函数
fn()
}