? ? ? ? 什么场景会用到effect嵌套呢?听我娓娓道来。
? ? ? ? 就用Vue.js来说吧,Vue.js的渲染函数就是在effect中执行的:
/*Foo组件*/
const Foo = {
render() {
return /*.....*/
}
}
// effect中执行Foo组件中的渲染函数
effect(() => {
Foo.render()
})
? ? ? ? 当组件发生嵌套的时候,渲染的时候effect函数中就会发生effect嵌套。
//Bar组件
const Bar = {
render() {/* ... */}
}
const Foo = {
render() {
return <Bar />
}
}
//effect执行的时候就会发生嵌套
effect(() => {
Foo.render()
effect(() => {
Bar.render()
})
})
? ? ? ? 所以,目前并不能支持effect嵌套,我们用之前的代码来测试一下:
//原始数据
const data = {
ok: true,
text: 'hello world'
}
// WeakMap桶
const bucketMap = new WeakMap()
//用全局变量存储被注册的副作用函数
let activeEffect
function effect(fn) {
const effectFn = () => {
cleanup(effectFn)
activeEffect = effectFn
fn()
}
// deps用来存储所有与这副作用函数相关联的依赖集合
effectFn.deps = []
effectFn()
}
const obj = new Proxy(data, {
get(target, key) {
// 注册副作用函数
track(target, key)
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
// 触发副作用函数
trigger(target, key)
return true
}
})
function cleanup(effectFn) {
//遍历effectFn的deps数组
for(let i = 0; i < effectFn.deps.length; i++) {
let deps = effectFn.deps[i]
deps.delete(effectFn)
}
// 最后需要重置effectFn.deps数组
effectFn.deps.length = 0
}
function trigger(target, key) {
// 取target
const depsMap = bucketMap.get(target)
if (!depsMap) return
// 根据key取副作用函数
const effects = depsMap.get(key)
// 执行副作用函数
const effectToRun = new Set(effects)
effectToRun && effectToRun.forEach(fn => fn())
}
function track(target, key) {
// 没有activeEffect直接返回
if (!activeEffect) return target[key]
// 取出WeakMap桶里的值 target ===> key
let depsMap = bucketMap.get(target)
// 如果不存在depsMap,那就新建Map与target建立联系
if (!depsMap) {
bucketMap.set(target, (depsMap = new Map()))
}
// key ===> effectFn
let deps = depsMap.get(key)
if (!deps) {
depsMap.set(key, deps = new Set())
}
// 注册副作用函数
deps.add(activeEffect)
// ======= 主要就是增加关联数组中 ===========
activeEffect.deps.push(deps)
}
let temp1, temp2
effect(function effectFn1() {
console.log('effectFn1执行')
effect(function effectFn2() {
console.log('effectFn2执行');
temp2 = obj.ok
})
temp1 = obj.text
})
obj.text = 'Vue3'
?
? ? ? ? 出现问题了,当我们修改text的值的时候,我们希望的是触发effectFn1,而现在是触发effectFn2,并没有执行effectFn1。
? ? ? ? 我们用全局变量activeEffect来存储通过effect函数注册的副作用函数,意味着同一时刻activeEffect所存储的副作用函数只能有一个。当副作用函数发生嵌套时,内层副作用函数会覆盖activeEffect,并且永远不会恢复,即使响应式数据是在外层副作用函数中读取的,他们收集到的副作用函数也都会是内层副作用函数,这个就是问题所在。
? ? ? ? 为了解决这个问题,我们需要一个副作用函数栈effectStack,在副作用函数执行的时候,将当前副作用函数压入栈中,执行完毕后将副作用函数弹出,activeEffect始终指向栈顶的副作用函数。这样便不会出现相互影响的情况了。
//用全局变量存储被注册的副作用函数
let activeEffect
// effectStack栈
let effectStack = []
function effect(fn) {
const effectFn = () => {
cleanup(effectFn)
activeEffect = effectFn
effectStack.push(effectFn)
fn()
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
}
// deps用来存储所有与这副作用函数相关联的依赖集合
effectFn.deps = []
effectFn()
}
? ? ? ? 完整代码:
//原始数据
const data = {
ok: true,
text: 'hello world'
}
// WeakMap桶
const bucketMap = new WeakMap()
//用全局变量存储被注册的副作用函数
let activeEffect
// effectStack栈
let effectStack = []
function effect(fn) {
const effectFn = () => {
cleanup(effectFn)
activeEffect = effectFn
effectStack.push(effectFn)
fn()
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
}
// deps用来存储所有与这副作用函数相关联的依赖集合
effectFn.deps = []
effectFn()
}
const obj = new Proxy(data, {
get(target, key) {
// 注册副作用函数
track(target, key)
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
// 触发副作用函数
trigger(target, key)
return true
}
})
function cleanup(effectFn) {
//遍历effectFn的deps数组
for(let i = 0; i < effectFn.deps.length; i++) {
let deps = effectFn.deps[i]
deps.delete(effectFn)
}
// 最后需要重置effectFn.deps数组
effectFn.deps.length = 0
}
function trigger(target, key) {
// 取target
const depsMap = bucketMap.get(target)
if (!depsMap) return
// 根据key取副作用函数
const effects = depsMap.get(key)
// 执行副作用函数
const effectToRun = new Set(effects)
effectToRun && effectToRun.forEach(effectFn => effectFn())
}
function track(target, key) {
// 没有activeEffect直接返回
if (!activeEffect) return target[key]
// 取出WeakMap桶里的值 target ===> key
let depsMap = bucketMap.get(target)
// 如果不存在depsMap,那就新建Map与target建立联系
if (!depsMap) {
bucketMap.set(target, (depsMap = new Map()))
}
// key ===> effectFn
let deps = depsMap.get(key)
if (!deps) {
depsMap.set(key, deps = new Set())
}
// 注册副作用函数
deps.add(activeEffect)
// ======= 主要就是增加关联数组中 ===========
activeEffect.deps.push(deps)
}
let temp1, temp2
effect(function effectFn1() {
debugger
console.log('effectFn1执行')
effect(function effectFn2() {
console.log('effectFn2执行');
temp2 = obj.ok
})
temp1 = obj.text
})
obj.text = 'Vue3'
????????
? ? ? ? 这样便达到预期!!?