响应式
可以自动响应数据变量的代码机制,我们就称之为是响应式。如:当有一个值发生了变化,引用了这个值的地方会自动重新执行
响应式的实现
我们已经知道了其原理,那么我们如何实现对象的响应式呢?
我们看看我们需要做什么?对象的属性发生变化时,我们需要将要做响应式的代码,重新执行。
我们先来分析步骤。
- 我们我们需要实现一个响应式函数(他接收需要响应式的函数)
- 响应式依赖收集(第一反应就是用数组管理,但数组又太难管理,所以可以用类)
- 监听对象的变化(Object.defineProperty或者用proxy)
- 当变化后,重新执行我们刚刚保存的依赖。
我们先来一个最简单的(就是什么都不考虑,只要达到效果就行)
let obj = {
name: 'xt'
}
// 依赖收集
const dependArr = []
// 响应式函数
function watchFn(fn) {
dependArr.push(fn)
fn()
}
watchFn(function() {
console.log('我是要响应的内容');
})
Object.keys(obj).forEach(key => {
let val = obj[key]
Object.defineProperty(obj, key, {
get: function() {
return val
},
set: function(newVal) {
dependArr.forEach(fn => fn())
val = newVal
}
})
})
obj.name = 'tx'
如上,当我们obj.name发生了变化 我们传入进在watchFn中的函数就会重新执行,接下来,我们就只是需要在这上面做拓展(接下来将会使用proxy的方式)
// 当前需要收集的响应式函数
let activeReactiveFn = null
// 响应式的函数
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 依赖类
class Depend {
constructor() {
// 依赖容器
this.reactiveFns = new Set()
}
// 添加依赖的方法
depend() {
activeReactiveFn && this.reactiveFns.add(activeReactiveFn)
}
// 执行依赖的方法
notify() {
this.reactiveFns.forEach(fn => {
fn()
})
}
}
// 获取当前依赖 数据结构:WeakMap中的属性名是对象,值为map map中的属性名是对象的属性名,值为new Depend()
const targetMap = new WeakMap()
function getDepend(target, key) {
let map = targetMap.get(target)
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
}
// 将对象变为响应式
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
// 收集依赖
const depend = getDepend(target, key)
depend.depend()
return Reflect.get(target, key, receiver)
},
set(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
// 获取当前依赖,并重新执行
const depend = getDepend(target, key)
depend.notify()
}
})
}
const infoProxy = reactive({
name: "xt", // depend对象
age: 18 // depend对象
})
// 当infoProxy.address变化时需要执行的代码 当其首次执行行 我们需要收集依赖
watchFn(() => {
console.log(infoProxy.name)
})
watchFn(() => {
console.log(infoProxy.age)
})
infoProxy.name = "tx"
infoProxy.name = "20"
总结步骤:
- 我们需要将一个普通对象转为一个响应式对象,即通过reactive函数,他接收一个普通对象,返回的是一个proxy对象。
- 在reactive函数中,我们需要用到porxy的两个捕获器get与set,get是用来收集依赖的,当我们对象的某个属性值变化时,我们可以在set捕获器中捕获到,让他重新执行需要响应的函数
- 不管我们set还是get,我们都需要找到,对应的依赖来执行,而不是乱执行,所以我们需要用一个合适的数据结构来存取我们对应的依赖(数据结构:WeakMap中的属性名是对象,值为map map中的属性名是对象的属性名,值为new Depend())