mini-vue(01)

77 阅读2分钟

一. 如何创建一个响应式对象?

在vue3中通过使用Proxy创建一个对象的代理,从而实现对对象基本操作的拦截

const target = {age:10}
const observed = new Proxy(target,baseHandler)

二. 响应式对象的get和set

2.1 get

当访问observed的某个属性时,会触发get操作

const target = { age: 10 }
const observed = new Proxy(target, {
    get(target, key) {
        console.log('触发get')
        const res = Reflect.get(target, key)
    }
})

observed.age

2.2 set

当设置observed的某个属性时,会触发set操作

const target = { age: 10 }
const observed = new Proxy(target, {
    get(target, key) {
        console.log('触发get')
        const res = Reflect.get(target, key)
        return res
    },
    set(target,key,value){
        console.log('触发set')
        const res = Reflect.set(target, key,value)
        return res
    }
})

observed.age++

三. 依赖收集与触发依赖

在vue3中当读取响应式对象的某个属性时,会触发Proxy的get操作,此时可在get中收集依赖。当修改某个属性时触发set操作,在此时重新触发依赖

在get中收集依赖,在set中触发依赖

image.png

const target = { age: 10 }
const observed = new Proxy(target, {
    get(target, key) {
        console.log('依赖收集')
        track(target,key)
        const res = Reflect.get(target, key)
        return res
    },
    set(target,key,value){
        console.log('触发依赖')
        tigger(target,key)
        const res = Reflect.set(target, key,value)
        return res
    }
})

3.1 存储依赖的容器

定义一个全局的Map。const targetMap = new Map() targetMap的键就是原始对象target,值也是一个Map,这个Map的键是对象的某个属性,值为依赖于此属性的依赖

image.png

3.2 依赖

进行依赖收集,首先要定义依赖。我们可以通过定义一个ReactiveEffect类,然后new ReactiveEffect得到一个依赖。

const user = reactive({ age: 10 }) // 创建响应式对象
effect(() => { newAge = user.age + 1 })

function effect(fn){
   const _effect =  new ReactiveEffect(fn)
   _effect.run()
}

class ReactiveEffect{
    private _fn: any;
    constructor(fn) {
    this._fn = fn
    }
    run() {
        activeEffect = this  // 在track中收集这个依赖
        const result = this._fn() // 执行传入的fn,触发依赖收集
        return result
    }
}

3.3 track

const targetMap = new Map()
function track(target,key){
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        depsMap = new Map()
        targetMap.set(target, depsMap)
    }
    let dep = depsMap.get(key)
    if (!dep) {
        dep = new Set()
        depsMap.set(key, dep)
    }
    if(dep.has(activeEffect))return // 已经存了就跳过
    dep.add(activeEffect)
}

3.4 trigger

function trigger(target,key){
    const depsMap = tragetMap.get(target)
    const dep = depsMap.get(key)
    for(const effect of dep){
        effect.run() // 再次执行用户传入的fn
    }
}