1. Vue3(Proxy)
- 使用 Proxy 接管我们要监听的对象
这样我们就可以在我们定义的代理对象上面执行对应的操作。
const obj = {
name: 'xxx',
code: 'xxx'
}
const objProxy = new Proxy(obj, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
}
})
- 创建依赖类
class Depend {
constructor() {
this.reactiveFns = []
}
// 将需要执行的函数添加到执行队列中
addDepend(reactiveFn) {
this.reactiveFns.push(reactiveFn)
}
// 发送通知
notify() {
this.reactiveFns.map(fn => fn())
}
}
- 封装一个响应式函数
const depend = new Depend()
watch(fn => {
depend.addDepend(fn)
})
- 在设置完新的值后执行对应的函数
...
set(target, key, newValue, receiver) {
depend.notify()
...
}
- 为不同的对象设置不同的依赖
因为全局不可能只有一个对象,所以我们根据不同的对象建立不同的 Map;在 Map 对象为不同的属性建立不同的 Depend 类。
const targetMap = new WeakMap()
const getDepend(target, key) {
let map = targetMap.get(target)
// 如果一开始没有对应的 Map, 我们需要手动创建一个 Map
if (!map) {
map = new Map()
targetMap.set(target, map)
}
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
- 更新 set 函数
set(target, key, newValue, receiver) {
const depend = getDepnd(target, key)
depend.notify()
...
}
- 更新响应式函数
在这一步,我们在监听对应的函数时,即在 get 时并不知道要执行什么函数,但是在 watchFn 时知道执行了什么函数,所以我们创建一个全局变量,拿到此时要执行的函数
let activeReactiveFn = null
watchFn(fn => {
activeReactiveFn = fn
fn()
// 防止内存泄漏,在执行完毕后记得清空引用
activeReactiveFn = null
})
get(target, key) {
const depend = getDepend(target, key)
if (activeReactiveFn) {
depend.addDepend(activeReactiveFn)
}
}
- 优化代码
let activeReactiveFn = null
// 为防止同一个属性执行多次函数,我们使用 Set 保存 reactiveFns,而不是数组。
class Depend {
constructor() {
this.reactiveFns = new Set()
}
// 将需要执行的函数添加到执行队列中
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
// 发送通知
notify() {
Array.from(this.reactiveFns).map(fn => fn())
}
}
- 完整代码
let activeReactiveFn = null
/**
* depend 优化
* 1> 使用 depend 类方法
* 2> 使用 set 而不是 数组
*/
class Depend {
constructor() {
this.reactiveFns = new Set()
}
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
Array.from(this.reactiveFns).map(fn => fn())
}
}
// 封装一个获取 depend 的函数
/**
* 需要用到两层 Map
* 第一层 Map 是保存 不同的对象
* 第二层 Map 是保存 上述对象中不同属性值的依赖
*/
const targetMap = new WeakMap()
const getDepend = (target, key) => {
// 根据 target 获取 map 的过程
let map = targetMap.get(target)
// 如果第一层 Map 首次没找到,新建一个 Map
if (!map) {
map = new Map()
targetMap.set(target, map)
}
let depend = map.get(key)
// 如果第二层 Map 首次没找到依赖,新建 Depend
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
// 封装一个响应式的函数
const watchFn = fn => {
activeReactiveFn = fn
fn()
// 当此全局变量不用时,要置为空,防止内存泄漏
activeReactiveFn = null
}
const obj = {}
// 定义一个 Proxy 对象
const reactive = (obj) => {
return new Proxy(obj, {
get(target, key, reveiver) {
// 根据 target 、key 获取对应的依赖
const depend = getDepend(target, key)
// 给 depend 中添加响应函数
// depend.addDepend(activeReactiveFn)
depend.depend()
return Reflect.get(target, key, reveiver)
},
set(target, key, newValue, reveiver) {
Reflect.set(target, key, newValue, reveiver)
const depend = getDepend(target, key)
depend.notify()
}
})
}
const objProxy = new Proxy(obj, {
get(target, key, reveiver) {
// 根据 target 、key 获取对应的依赖
const depend = getDepend(target, key)
// 给 depend 中添加响应函数
// depend.addDepend(activeReactiveFn)
depend.depend()
return Reflect.get(target, key, reveiver)
},
set(target, key, newValue, reveiver) {
Reflect.set(target, key, newValue, reveiver)
const depend = getDepend(target, key)
depend.notify()
}
})
// 为防止多次调用,使用 set
watchFn(() => {
console.log(obj.name, '--------------')
console.log(obj.name, '++++++++++++++')
})
const info = reactive({
name: 'xxx',
code: 'xxx'
})
2. Vue2(Object.defineProperty)
const reactive = obj => {
Object.keys(obj).map(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get() {
// 根据 target 、key 获取对应的依赖
const depend = getDepend(obj, key)
depend.depend()
return value
},
set(newValue) {
value = newValue
const depend = getDepend(target, key)
depend.notify()
}
})
})
return obj
}