vue3源码学习(二)简单的依赖收集和派发更新
一、 原理
vue的模板本质上是个render函数,在初始化的过程中收集render函数中的响应式对象,并在响应式对象属性值发生变化时,重新执行render函数
二、实现
- 依赖收集:立即执行一个函数,将该函数收集到函数中涉及到的所有响应式对象中
- 派发更新:修改响应式对象的属性值时,执行收集在该响应式对象中所有的函数
定义一个依赖类,用于存储依赖和派发更新
let activeEffect // 建一个全局变量,用于存储当前正在收集依赖的 effect 函数
/**
* 定义一个依赖类,用于存储依赖和派发更新
*/
class Dep {
constructor() {
this.effects = new Set()
}
/**
* 依赖收集
*/
depend() {
if (activeEffect) {
this.effects.add(activeEffect)
}
}
/**
* 派发更新
*/
notify() {
this.effects.forEach((_effect) => {
_effect()
})
}
}
定义一个创建响应式对象的函数
- 在读取响应式对象属性时,触发依赖收集
- 在修改响应式对象属性时,派发更新
/**
* 创建响应式对象的函数
* @param {Object} obj
*/
function reactive(obj) {
const proxy = new Proxy(obj, {
get(target, prop) {
const result = Reflect.get(target, prop, proxy)
// 依赖收集
track(target, prop)
return result
},
set(target, prop, value) {
const result = Reflect.set(target, prop, value, proxy)
// 派发更新
trigger(target, prop)
return result
},
})
return proxy
}
实现依赖收集(track)和派发更新(trigger)
// 所有依赖集合
const targetMap = new WeakMap()
// 收集依赖
function track(target, prop) {
let depMap = targetMap.get(target)
if (!depMap) {
depMap = new Map()
targetMap.set(target, depMap)
}
let dep = depMap.get(prop)
if (!dep) {
dep = new Dep()
depMap.set(prop, dep)
}
dep.depend()
}
// 派发更新
function trigger(target, prop) {
const depMap = targetMap.get(target)
const dep = depMap.get(prop)
if (dep) {
dep.notify()
}
}
定义一个 effect 函数,用于收集依赖
- 传入一个函数,设置为当前正在收集依赖的
effect
函数。 - 执行该函数,执行函数时,会触发函数中
所有响应式对象
的get
函数 ,从而将当前effect
函数收集到当前响应式对象的依赖集合里。 - 将当前正在收集依赖的
effect
函数置为null
/**
* 定义一个 effect 函数,用于收集依赖
* @param {function} fn
*/
function effect(fn) {
activeEffect = fn
fn()
activeEffect = null
}
使用示例
// 定义响应式对象
const person = reactive({
name: '张三',
age: 18,
})
// 依赖收集
effect(() => {
console.log(`person -> ${person.name} -> ${person.age}`)
})
effect(() => {
console.log(`${person.name} -> ${person.age} -> person`)
})
// 修改响应式变量属性的值,派发更新
person.name = '李四'
person.age = 20
完整代码
let activeEffect // 建一个全局变量,用于存储当前正在收集依赖的 effect 函数
/**
* 定义一个依赖类,用于存储依赖和派发更新
*/
class Dep {
constructor() {
this.effects = new Set()
}
/**
* 依赖收集
*/
depend() {
if (activeEffect) {
this.effects.add(activeEffect)
}
}
/**
* 派发更新
*/
notify() {
this.effects.forEach((_effect) => {
_effect()
})
}
}
// 所有依赖集合
const targetMap = new WeakMap()
// 收集依赖
function track(target, prop) {
let depMap = targetMap.get(target)
if (!depMap) {
depMap = new Map()
targetMap.set(target, depMap)
}
let dep = depMap.get(prop)
if (!dep) {
dep = new Dep()
depMap.set(prop, dep)
}
dep.depend()
}
// 派发更新
function trigger(target, prop) {
const depMap = targetMap.get(target)
const dep = depMap.get(prop)
if (dep) {
dep.notify()
}
}
/**
* 创建一个响应式对象
* @param {Object} obj
*/
function reactive(obj) {
const proxy = new Proxy(obj, {
get(target, prop) {
const result = Reflect.get(target, prop, proxy)
track(target, prop)
return result
},
set(target, prop, value) {
const result = Reflect.set(target, prop, value, proxy)
trigger(target, prop)
return result
},
})
return proxy
}
/**
* 定义一个 effect 函数,用于收集依赖
* @param {function} fn
*/
function effect(fn) {
activeEffect = fn
fn()
activeEffect = null
}
// 使用示例
const person = reactive({
name: '张三',
age: 18,
})
effect(() => {
console.log(`person -> ${person.name} -> ${person.age}`)
})
effect(() => {
console.log(`${person.name} -> ${person.age} -> person`)
})
person.name = '李四'
person.age = 20
转载自:https://juejin.cn/post/7352079327117377548