响应式数据与副作用函数
副作用函数就是会产生副作用的函数, 例如:
function effect () {document.body.innerText = 'hello vue3'}
当函数 effect
运行后, 会设置 body
的文本使其扭转, 而其余函数也能够扭转与获取 body
中的文本, 也就是说 effect
函数的执行间接或间接影响了其余其余函数的执行后果, 这是 effect
的副作用. 副作用函数有很多也十分常见比方扭转某一个全局变量的值.
响应式的根本思维
const obj = {text: 'hello world'}
function effect () {document.body.innerText = obj.text}
obj.text = 'hello no world'
如上代码, 副作用函数 effect
执行时会触发 obj.text
的读取 [[GET]]
操作, 而对 obj.text
赋值时会触发设置 [[SET]]
操作, 因而读取操作时, 能够把副作用函数 effect
存储到某一数据结构中, 当设置时能够把副作用函数 effect
取出在运行, 这就实现了最根本的响应式如下图:
对于监听读取与设置操作, 能够应用代理对象 Prosy
实现:
// 存储副作用函数
const bucket = new Set()
// 原始数据
const data = {text: 'hello world'}
// 解决原函数数据
const obj = new Proxy(data, {
// 拦挡读取操作
get (target, key) {
// 存储副作用函数 effect
bucket.add(effect)
// 返回属性值
return target[key]
},
// 拦挡设置操作
set (target, key, newVal) {console.log(newVal);
// 设置属性值
target[key] = newVal
// 把副作用函数取出并执行
bucket.forEach(fn => fn())
// 返回 true 代表操作胜利
return true
}
})
目前尽管能够实现, 然而还有许多缺点, 比方这里间接通过名字获取副作用函数, 这种硬编码的形式不灵便, 并且没有在副作用函数与被操作的指标对象的属性没有建设明确的关系, 此时若读取 obj
中的属性, 都会把副作用函数存储, 若设置某一个属性都会把副作用函数取出并执行, 因而能够应用某一全局变量贮存副作用属性:
// 用于贮存被注册的副作用的函数
let activeEffect = undefined
function effect (fn) {
activeEffect = fn
fn()}
并应用 WeakMap, Map, Set
别离贮存:
之所以应用 WeakMap
作为贮存副作用函数的容器, 是因为当 WeakMap
对key
是弱援用, 所援用的对象不存在时, 会主动被垃圾回收机制回收避免内存溢出:
// 贮存副作用函数的桶
const bucket = new WeakMap()
// 用于贮存被注册的副作用的函数
let activeEffect = undefined
function effect (fn) {
activeEffect = fn
fn()}
const data = {text: 'hello world'}
const obj = new Proxy(data, {
// 拦挡读取操作
get (target, key) {
// 没有 activeEffect, 间接 return
if (!activeEffect) return target[key]
// 依据 target 从 '桶' 中回去 depsMap, 它也是一个 Map 类型: key ---> effects
let depsMap = bucket.get(target)
// 如果 depsMap 不存在, 则新建一个 Map 并与 target 关联
if (!depsMap) bucket.set(target, (depsMap = new Map()))
// 再依据 key 从 depsMap 中去的 deps, 它是一个 Set 类型
// 外面存贮所有与以后 key 相干的副作用函数: effects
let deps = depsMap.get(key)
// 如果 deps 不存在, 同样新建一个 Set 并与 key 关联 0
if (!deps) depsMap.set(key, (deps = new Set()))
// 最初将以后激活的副作用函数增加到 '桶' 里
deps.add(activeEffect)
// 返回属性值
return target[key]
},
// 拦挡设置操作
set (target, key, newVal) {
// 设置属性值
target[key] = newVal
// 依据 target 从 '桶' 中获得 depsMap, 它是 key --> effects
const depsMap = bucket.get(target)
if (!depsMap) return
// 依据 key 获得所有的副作用函数 effects
const effects = depsMap.get(key)
// 执行副作用函数
effects && effects.forEach(fn => fn())
}
})
#### 读取操作流程
设置操作流程