用最简单的代码实现vue3源码中的reactive、effect、computed

7 篇文章 0 订阅
4 篇文章 0 订阅

最近在学习vue3源码中的一些知识点,从源码层面来讲,发现vue3对比vue2来说,vue3更加精巧,代码量更少,这也就意味着有些地方需要花费更多的精力去理解,在网上找了源码视频学习了一下,自己花了150多行代码实现一下reactive、effect、computed,这只是最简单的实现,可以将代码直接复制运行;不需要任何配置~~

function reactive(target) { // reactive本质-返回一个proxy实例
    return new Proxy(target, {
        get(target, key, receiver) {
            track(target, key)
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
            trigger(target, key)
            return res
        }
    })
}
let targetMap = new WeakMap() // 依赖集合,实际上是一个类似json的map结构
/**最外层是一个WeakMap结构;target就是各个被proxy的对象,作为键;key就是各个属性,每个属性对应一个set,存储该属性关联的依赖
 * {
 *      target1 : {
 *          key: [
 *                  effect1,
 *                  effect2
 *               ] 
 *      },
 *      target2: {
 *           key1 : [],
 *           key2: []
 *      }
 *
 * }
 * */
let activeEffect; // 当前effect(观察者)
const state = reactive({
    name: 'xxxx',
    age: 18
})


function effect(fn) { // effect函数实际上创建一个ReactiveEffect类的实例 ,并执行该实例的run方法
    const _effect = new ReactiveEffect(fn)
    _effect.run()
}


class ReactiveEffect { // 实际上就是一个观察者
    active = true
    deps = []
    constructor(fn, scheduler) {
        this.fn = fn
        this.scheduler = scheduler
    }
    run() {
        try {
            activeEffect = this
            return this.fn()
        } finally {
            activeEffect = null
        }
    }
}



function track(target, key) { // 依赖收集
    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())
    }
    trackEffects(dep)
}

function trackEffects(dep) {
    const shouldTrack = !dep.has(activeEffect)
    if (shouldTrack) {
        dep.add(activeEffect)
        activeEffect.deps.push(dep)
    }
}

function trigger(target, key) { // 更新依赖-类似vue2种的notify
    const depsMap = targetMap.get(target)
    if (!depsMap) return
    let deps = []
    if (key !== undefined) {
        deps.push(depsMap.get(key))
    }
    const effects = []
    deps.forEach(dep => {
        effects.push(...dep)
    })
    triggerEffects(effects)
}

function triggerEffects(effects) {
    for (let effect of effects) {
        if (effect !== activeEffect) {
            if (effect.scheduler) {
                return effect.scheduler()
            }
            effect.run()
        }
    }
}

effect(() => {
    console.log(state.age) // state.age触发proxy中的get方法
})

setTimeout(() => {
    state.age = 28 // 触发proxy中的set方法,此时trigger(更新)
}, 3000);


function computed(getterOrOptions) { // computed本质就是返回一个ComputedRefImpl实例
    return new ComputedRefImpl(getterOrOptions)
}

class ComputedRefImpl { // computed类
    dep;
    _value;
    _dirty = true;
    effect; // computed本质就是存储一个观察者(effect)
    constructor(getter, setter = () => { }) {
        this.setter = setter
        this.effect = new ReactiveEffect(getter, () => {
            if (!this._dirty) {
                this._dirty = true
                triggerEffects(this.dep)
            }
        })
    }
    /**
     * 通过类的 get 方法获取属性
     */
    get value() { // 通过获取computed.value属性 触发effect.run() 
        trackEffects(this.dep || (this.dep = new Set()))
        if (this._dirty) {
            this._value = this.effect.run()
            this._dirty = false
        }
        return this._value // computed.value本质就是 _value
    }

    set value(val) {
        this.setter(val)
    }
}

const myAge = computed(() => {
    return state.age + 15 // 获取state.age时会触发proxy的get方法,此时age对应的dep会收集computed中创建的effect(观察者)
})

effect(() => {
    console.log(myAge.value) // myAge.value本质调用 computedRefImpl中的get value()方法,并获取返回值
})

console.log(targetMap)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值