vue3 effect实现

effect 实现

需要考虑的场景

1.用 标识,在effect嵌套,多个并排effect的格式下判断是否为 effect内部的代理对象的变量
2.用 映射表,双向标记,完成依赖收集,解决 属性 和 effect 的 多对多的关系

实现结果


export function effect(fn) {// 副作用函数
    const _effect = new ReactiveEffect(fn)
    _effect.run() // 默认让响应式的effect执行
}
// 核心类
let activeEffect;
class ReactiveEffect {
    public active = true;
    public deps = [];//依赖收集项
    public parent = undefined;
    constructor(public fn) {}
    run() {
        if (!this.active) {
            return this.fn(); // 直接执行此函数即可
        }
        // 其他情况下 意味着是激活的状态
        // 1.针对 effect 嵌套的格式,使用标记判断是否为 effect内部的代理对象的变量
        try { // tree树父子关系
            this.parent = activeEffect;
            activeEffect = this;
            return this.fn() // 会取响应式的数据
        } finally { // 方法执行完 去掉标记
            activeEffect = this.parent
            this.parent = undefined
        }
        // 2.对于 多个effect并排的情况,要方便区别变量是否在effect内部
    }
}
// 映射表
const targetMap = new WeakMap()
export function track(target, key) {
    if (!activeEffect) {
        // 取值操作没有发生在effect中
        return;
    }
    debugger
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()));
    }
    let dep = depsMap.get(key);
    if(!dep){
        depsMap.set(key,(dep = new Set()))
    }
    let shouldTrack = !dep.has(activeEffect)
    if(shouldTrack){
        // 双向的标记
        // 一个属性可能对应多个 effect,一个effect 可能对应多个属性
        // 属性 和 effect 的关系时多对多
        dep.add(activeEffect);
        activeEffect.deps.push(dep);//依赖收集
    }
}

// 在getter 中触发 track 依赖收集 处理
get(target, key, receiver) {// 目标   键名   当前代理对象
     if (ReactiveFlags.IS_REACTIVE == key) {
        // 第一次 target[ReactiveFlags.IS_REACTIVE]时,未触发getter,当代理已代理对象,会触发已代理的 getter
        return true
     }
    track(target,key)
    return Reflect.get(target, key, receiver) // 处理了 this 问题
},

场景1

在effect嵌套格式

const state = reactive({name: 'jw', age: 30});
effect(() => {
   app.innerHTML = state.name + state.age
      effect(() => {
         app.innerHTML = state.name + state.age
      })
})

多个并排effect的格式

effect(() => {
   document.getElementById('app').innerHTML = state.name + state.age
})
effect(() => {
   document.getElementById('app').innerHTML = state.name + state.age
})

解决方案: parent 标识

// 1.针对 effect 嵌套的格式和多个effect并排的情况
// 使用 activeEffect 标记,判断是否为 effect内部的回调
try { // 方法执行时 加上 标记 为 当前 实例 类
    this.parent = activeEffect;
    activeEffect = this;
    return this.fn() // 会取响应式的数据
} finally { // 方法执行完,若在最外层则去掉标记
    activeEffect = this.parent
    this.parent = undefined
}

... 

if (!activeEffect) {
    // 取值操作没有发生在effect中
    return;
}

场景2

属性 和 effect 的 多对多的关系

// 一个 effect 对 name,age 属性
effect(() => {
    app.innerHTML = state.name + state.age
})
// 多个 effect 对 name 属性
effect(() => {
    document.getElementById('app').innerHTML = state.name
})
effect(() => {
    document.getElementById('app').innerHTML = state.name
})

解决方案:映射表

//let mapping = { // 映射表格式 
//     target: {
//         key: [activeEffect]
//     }
// }
// 映射表
const targetMap = new WeakMap() 
export function track(target, key) {
    if (!activeEffect) {
        // 取值操作没有发生在effect中
        return;
    }
    debugger
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()));
    }
    let dep = depsMap.get(key);
    if (!dep) {
        depsMap.set(key, (dep = new Set()))
    }
    let shouldTrack = !dep.has(activeEffect)
    if (shouldTrack) {
        // 双向的标记
        // 一个属性可能对应多个 effect,一个effect 可能对应多个属性
        // 属性 和 effect 的关系时多对多
        dep.add(activeEffect);
        activeEffect.deps.push(dep);//依赖收集
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值