Skip to content

Vue.js 设计与实现-响应系统-Ref

作者:江月迟迟
发表于:2024-12-10
字数统计:14207 字
预计阅读48分钟

(留坑)这里的代码疑似有BUG

(填坑)BUG已解决,等待回归

(填坑2)发现新BUG,解决新bug

在本章中,我们首先介绍了 ref的概念。ref本质上是一个“包裹对象”。因为 JavaScript的 Proxy无法提供对原始值的代理,所以我们需要使用一层对象作为包裹,间接实现原始值的响应式方案。由于“包裹对象”本质上与普通对象没有任何区别,因此为了区分 ref与普通响应式对象,我们还为“包裹对象”定义了一个值为 true的属性,即__v_isRef,用它作为 ref的标识。

ref除了能够用于原始值的响应式方案之外,还能用来解决响应丢失问题。为了解决该问题,我们实现了 toRef以及 toRefs这两个函数。它们本质上是对响应式数据做了一层包装,或者叫作“访问代理”。

最后,我们讲解了自动脱 ref的能力。为了减轻用户的心智负担,我们自动对暴露到模板中的响应式数据进行脱 ref处理。这样,用户在模板中使用响应式数据时,就无须关心一个值是不是 ref了。

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>

<script>
// 定义区
let activeEffect
const effectStack = []

const reactiveMap = new Map()

const arrayInstrumentations = {}

function ref(val) {
    // 在ref函数内部创建一个包裹对象
    const wrapper = {
        value: val     
    }
    // 使用Object.defineProperty在wrapper上定义一个不可枚举的__v_isRef属性,并且值为true
    Object.defineProperty(wrapper, '__v_isRef', {
        value: true
    })
    // 将包裹对象变成响应式数据
    return reactive(wrapper)
}

function toRef(obj, key) {
    const wrapper = {
        get value() {
            return obj[key]
        }
    }
    return wrapper
}

function toRefs(obj) {
    const ret = {}
    for(const key in obj) {
        ret[key] = toRef(obj, key)
    }
    return ret
}

function proxyRefs(target) {
    return new Proxy(target, {
        get(target, key, receiver) {
            const value = Reflect.get(target, key, receiver)
            // 自动脱ref实现:如果读取的值是ref,则返回他的value属性值
            return value.__v_isRef ? value.value : value
        },
        set(target, key, newValue, receiver) {
            // 通过target读取真实值
            const value = target[key]
            // 如果值是ref,则设置其对应的value属性值
            if (value.__v_isRef) {
                value.value = newValue
                return true
            }
            return Reflect.set(target, key, newValue, receiver)
        }
    })
}

;['includes', 'indexOf', 'lastIndexOf'].forEach(method => {
    const originMethod = Array.prototype[method]
    arrayInstrumentations[method] = function(...args) {
        // this时代理对象,现在代理对象中查找,将结果存储到res中
        let res = originMethod.apply(this, args)

        if (res === false || res === -1) {
            // res 为false说明没找到,通过this.raw拿到原始数组,再去其中查找并更新res值
            res = originMethod.apply(this.raw, args)
        }
        return res
    }
})

// 一个标记变量,代表是否进行追踪。默认值为true,即允许追踪
let shouldTrack = true
// 重写数组的push方法
;['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {
    // 取得原始push方法
    const originMethod = Array.prototype[method]
    // 重写
    arrayInstrumentations[method] = function(...args) {
        // 在调用原始方法之前,禁止追踪
        shouldTrack = false
        // push方法的默认行为
        let res = originMethod.apply(this, args)
        // 在调用原始方法之后,恢复原来的行为,即允许追踪
        shouldTrack = true
        return res
    }
})

// 抽离为独立的函数,便于复用
function iterationMethod() {
    // 获取原始数据对象 target
    const target = this.raw
    // 获取原始迭代器方法
    const itr = target[Symbol.iterator] ()

    const wrap = (val) => typeof val === 'object' && val !== null ? reactive(val) : val

    // 调用track函数建立响应联系
    track(target, ITERATE_KEY)

    // 返回自定义的迭代器
    return {
        next() {
            // 调用原始迭代器的nextfangfa获取value和done
            const { value, done } = itr.next()
            return {
                // 如果value不是undefined,则对其进行包裹
                value: value ? [wrap(value[0]), wrap(value[1])] : value,
                done
            }
        },
        // 并且实现可迭代协议
        [Symbol.iterator] () {
            return this
        }
    }
}

function valuesIterationMethod() {
    // 获取原始数据对象
    const target = this.raw
    // 通过target.values获取原始迭代器方法
    const itr = target.values()

    const wrap = (val) => typeof val === 'object' ? reactive(val) : val

    track(target, ITERATE_KEY)

    // 将其返回
    return {
        next() {
            const { value, done } = itr.next()
            return {
                // value是值,而非键值对
                value: wrap(value),
                done
            }
        },
        [Symbol.iterator] () {
            return this
        }
    }
}

const MAP_KEY_ITERATE_KEY = Symbol()

function keysIterationMethod() {
    // 获取原始数据对象
    const target = this.raw
    // 通过target.keys获取原始迭代器方法
    const itr = target.keys()

    const wrap = (val) => typeof val === 'object' ? reactive(val) : val

    track(target, MAP_KEY_ITERATE_KEY)

    // 将其返回
    return {
        next() {
            const { value, done } = itr.next()
            return {
                // value是值,而非键值对
                value: wrap(value),
                done
            }
        },
        [Symbol.iterator] () {
            return this
        }
    }
}

const mutableInstrumentations = {
    add(key) {
        // this 仍然指向的是代理对象,通过raw属性获取原始数据对象
        const target = this.raw
        // 先判断值是否已经存在
        const hadKey = target.has(key)

        // 通过原始数据对象执行add方法添加具体的值
        // 注意,这里不再需要.bind了,因为是直接通过targeet调用并执行的
        const res = target.add(key)
        if (!hadKey) {
            // 通过调用trigger函数触发响应,并指定操作类型为ADD
            trigger(target, key, 'ADD')
        }
        return res
    },

    delete(key) {
        const target = this.raw
        const hadKey = target.has(key)
        const res = target.delete(key)
        if (hadKey) {
            trigger(target, key, 'DELETE')
        }
        return res
    },

    get(key) {
        // 获取原始对象
        const target = this.raw
        // 判断读取的key是否存在
        const had = target.has(key)
        // 追踪依赖,建立响应联系
        track(target, key)
        // 如果存在,则返回结果。这里要注意的是,如果得到的结果res仍然是可代理的数据
        // 则要用reactive返回包装后的响应式数据
        if (had) {
            const res = target.get(key)
            return typeof res === 'object' ? reactive(res) : res
        }
    },

    set(key, value) {
        const target = this.raw
        const had = target.has(key)
        const oldValue = target.get(key)

        // 获取原始数据,由于value本身可能已经是原始数据,所以此时value.raw不存在,则直接使用value
        const rawValue = value.raw || value
        
        target.set(key, rawValue)
        if (!had) {
            trigger(target, key, 'ADD')
        } else if (oldValue !== value || (oldValue === oldValue && value === value)) {
            trigger(target, key, 'SET')
        }
    },

    forEach(callback, thisArg) {
        // wrap函数用来把可代理的值转换为响应式数据
        const wrap = (val) => typeof val === 'object' ? reactive(val) : val
        // 取得原始数据对象
        const target = this.raw
        // 与ITERATE_KEY建立响应联系
        track(target, ITERATE_KEY)
        // 通过原始数据对象调用forEach方法
        target.forEach((v, k) => {
            // 手动调用callback,用wrap方法包裹value和key后在传给callback,这样实现深相应
            callback.call(thisArg, wrap(v), wrap(k), this)
        })
    },

    [Symbol.iterator]: iterationMethod,
    
    entries: iterationMethod,

    values: valuesIterationMethod,

    keys: keysIterationMethod
}

const bucket = new WeakMap()

// 定义一个任务队列
const jobQueue = new Set()
// 使用Promise.resolve() 创建一个promise实例,我们用它将一个任务添加到微任务队列
const p = Promise.resolve()

// 用一个标志代表是否正在刷新队列
let isFlushing = false

const ITERATE_KEY = Symbol()

function flushJob() {
    // 如果任务正在刷新,则什么都不做
    if (isFlushing) return
    // 设置为true,代表正在刷新
    isFlushing = true
    // 在微任务队列刷新jobQueue队列
    p.then(() => {
        jobQueue.forEach(job => job())
    }).finally(() => {
        // 结束后重置isFlushing
        isFlushing = false
    })
}

// 封装 createReactive函数,接受参数isShallow,代表是否为浅相应,默认为false,即非浅响应(深相应)
// 接受参数isReadonly 代表是否只读,默认为false,表示非只读,即可修改
function createReactive(obj, isShallow = false, isReadonly = false) {
    return new Proxy(obj, {
        // 拦截读取操作,接受第三个参数receiver
        get(target, key, receiver) {

            // 如果读取的是raw属性,则返回原始数据对象target
            if (key === 'raw') {
                return target
            }

            if (key === 'size') {
                // 如果读取的是size属性
                // 调用track函数建立响应联系
                track(target, ITERATE_KEY)
                // 通过指定第三个参数receiver为原始对象target从而修复问题
                return Reflect.get(target, key, target)
            }
            // 读取其他属性的默认行为
            // 将方法与原始数据对象target绑定后返回
            return mutableInstrumentations[key]
        }, 

        has(target, key) {
            track(target, key)
            return Reflect.has(target, key)
        },

        ownKeys(target) {
            // 如果操作目标target是数组,则使用length属性作为key并建立响应联系
            // 将副作用函数与ITERATE_KEY 关联
            Array.isArray(target) ? track(target, 'length') : track(target, ITERATE_KEY)
            return Reflect.ownKeys(target)
        },

        set(target, key, newValue, receiver) {
            // 拦截设置操作

            // 如果数据是只读的,打印警告信息,直接返回
            if (isReadonly) {
                console.warn(`属性 ${key} 是只读的`)
                return
            }

            // 先获取旧值
            const oldValue = target[key]

            // 如果属性不存在,则说明是添加新属性,否则时设置已有属性
            // type可能是数组或者对象
            // 数组:如果设置的索引值小于长度,那么是SET操作,否则是ADD操作
            const type = Array.isArray(target) ? Number(key) < target.length ? 'SET' : 'ADD' :
            Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
            // 设置属性值
            const res = Reflect.set(target, key, newValue, receiver)

            // target === receiver.raw 说明receiver就是target的代理对象
            if (target === receiver.raw) {
                // 比较新值和旧值,只要当不全等的时候才触发相应
                if (oldValue !== newValue) {
                    // 将type作为第三个参数传递给trigger函数
                    // target[key] = newValue // 使用了Reflect.set设置,不必使用此句
                    // 增加第四个参数,即触发响应的新值
                    trigger(target, key, type, newValue)
                }
            }
            
            return res
            
        },

        deleteProperty(target, key) {

            // 如果数据是只读的,打印警告信息,直接返回
            if (isReadonly) {
                console.warn(`属性 ${key} 是只读的`)
                return
            }

            // 检查被操作的属性是否时对象自己的属性
            const hadKey = Object.prototype.hasOwnProperty.call(target, key)
            
            // 使用Reflect.deleteProperty完成属性的删除
            const res = Reflect.deleteProperty(target, key)
            
            if (res && hadKey) {
                // 只有当被删除的属性是对象自己的属性并且成功删除时,才触发更新
                trigger(target, key, 'DELETE')
            }
            return res
        }
    })
}

function reactive(obj) {
    // 优先通过原始对象obj寻找之前创建的代理对象,如果找到了,直接返回已有的代理对象
    const existionProxy = reactiveMap.get(obj)
    if (existionProxy) {
        return existionProxy
    }
    // 否则,创建新的代理对象
    const proxy = createReactive(obj)
    // 存储到map中,从而米便重复创建
    reactiveMap.set(obj, proxy)
    return proxy
}

function shallowReactive(obj) {
    return createReactive(obj, true)
}

function readonly(obj) {
    return createReactive(obj, false, true)
}

function shallowReadonly(obj) {
    return createReactive(obj, true, true)
}

function track(target, key) {
    // 没有副作用 直接返回
    // 当禁止追踪时,直接返回
    if (!activeEffect || !shouldTrack) return
    
    let depsMap = bucket.get(target)
    if (!depsMap) bucket.set(target, depsMap = new Map())
    
    // dep 预期是text1
    let deps = depsMap.get(key)
    if (!deps) depsMap.set(key, deps = new Set())
    
    // 这样,修改某一属性就详细到代理的某一个对象的某个属性
    deps.add(activeEffect)
    // deps 就是一个与当前副作用函数存在联系的依赖集合
    activeEffect.deps.push(deps)
}

function trigger(target, key, type, newValue) {
    // 设置的时候 通知对应的修改函数
    let depsMap = bucket.get(target)
    if (!depsMap) return
    // 取得与key相关联的副作用函数
    let effects = depsMap.get(key)
    

    const effectsToRun = new Set()
    // 将与key相关联的副作用函数添加到effectsToRun
    effects && effects.forEach(effectFn => {
        if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
        }
    })
    // 只有当操作类型为ADD 或者 DELETE时,才触发与ITERATE_KEY相关联的副作用函数重新执行
    // 如果操作类型是SET,并且目标对象是Map类型的数据,也应该触发那些与ITERATE_KEY相关联的副作用函数重新执行
    if (type === 'ADD' || type === 'DELETE' || (type === 'SET' && Object.prototype.toString.call(target) === '[object Map]')) {
        // 取得与ITERATE_KEY相关联的副作用函数
        const iterateEffects = depsMap.get(ITERATE_KEY)
        // 将与ITERATE_KEY相关联的副作用函数也添加到effectsToRun
        iterateEffects && iterateEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }
    
    if (type === 'ADD' || type === 'DELETE' && (type === 'SET' && Object.prototype.toString.call(target) === '[object Map]')) {
        // 将与MAP_KEY_ITERATE_KEY相关联的副作用函数也添加到effectsToRun
        const iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY)
        iterateEffects && iterateEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }

    // 当操作类型为ADD并且目标对象是数组时,应该取出并执行属性相关联的副作用函数
    if (type === 'ADD' && Array.isArray(target)) {
        // 取出与length相关联的副作用函数
        const lengthEffects = depsMap.get('length')
        lengthEffects && lengthEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }

    // 如果操作目标是数组,并且修改了数组的length属性
    if (Array.isArray(target) && key === 'length') {
        // 对于索引大于或者等于新的length值的元素
        depsMap.forEach((effects, key) => {
            if (key >= newValue) {
                effects.forEach(effectFn => {
                    if (effectFn !== activeEffect) {
                        effectsToRun.add(effectFn)
                    }
                })
            }
        })
    }
    

    effectsToRun.forEach(item => {
        // 如果一个副作用函数存在调度器,则调用该调度器,并将副作用函数作为参数传递
        if (item.options.scheduler) {
            item.options.scheduler(item.func)
        } else {
            item.func()
        }
    })
}

function effect(fn, options = {}) {
    // 这个effectFn是副作用函数
    const effectFn = {
        func: () => {
            cleanup(effectFn)
            // 当调用effect注册副作用函数时,将副作用函数赋值给activeEffect
            activeEffect = effectFn
            // 在调用副作用函数之前将当前副作用函数压入栈中
            effectStack.push(effectFn)
            // 将fn的执行结果存储到res中
            const res = fn()
            // 在调用副作用函数之后,将当前副作用函数弹出栈,并把activeEffect还原成之前的值
            effectStack.pop()
            activeEffect = effectStack[effectStack.length - 1]
            return res
        }
    }
    // 将options挂载到effectFn上
    effectFn.options = options
    // activeEffect.deps用来存储所有与该副作用函数相关联的依赖集合
    effectFn.deps = []
    if (!options.lazy) {
        effectFn.func()
    }
    return effectFn.func
}

function computed(getter) {
    // 使用value来缓存上一次计算的值
    let value
    // dirty标志,用来标识是否需要重新计算值,为true代表脏,需要重新计算
    let dirty = true
    // 把getter作为副作用函数,创建一个lazy的effect
    const effectFn = effect(getter, {
        lazy: true,
        // 添加调度器,在调度器中将dirty重置为true
        scheduler() {
            // 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发相应
            if (!dirty) {
                dirty = true
                trigger(obj, 'value')    
            }
            
        }
    })
    const obj = {
        // 当读取到value才执行effectFn
        get value() {
            if (dirty) {
                value = effectFn()
                // 将dirty设置为false,下一次访问直接使用缓存到value的值
                dirty = false
            }
            // 当读取value时,手动调用track函数进行追踪
            track(obj, 'value')
            return value
        }
    }
    return obj
}

// watch 函数接收两个参数,source是响应式数据,cb是回调函数,当监控到source数据变化时,cb执行
function watch(source, cb, options = {}) {
    // 定义getter
    let getter
    // 如果source是函数,说明用户传递的是getter,所以直接把source赋值给getter
    if (typeof source === 'function') {
        getter = source
    } else {
        getter = () => traverse(source)
    }
    // 定义旧值和新值
    let oldValue, newValue
    // 使用effect注册副作用函数时,开启lazy选项,并把返回值存储到effectFn中以便后续手动调用
    
    // cleanup用来存储用户注册的过期回调
    let cleanup
    // 定义onInvalidate函数
    function onInvalidate(fn) {
        cleanup = fn
    }


    // 提取scheduler调度函数为一个独立的job函数
    const job = () => {
        // 在scheduler中重新执行副作用函数,得到的是新值
        newValue = effectFn()
        // 在调用回调函数cb之前,先调用过期回调
        if (cleanup) {
            cleanup()
        }

        // 当数据变化时,调用回调函数cb
        // 将旧值和新值作为回调函数的参数
        // 将onInvalidate作为回调函数的第三个参数,以便用户使用
        cb(newValue, oldValue, onInvalidate)
        // 更新旧值,不然下一次会得到错误的旧值
        oldValue = newValue
    }

    const effectFn = effect(
        // 触发读取操作,从而建立联系
        () => getter(),
        {
            lazy: true,
            // 使用job函数作为调度器函数
            scheduler: () => {
                // 在调度函数中判断flush是否为post,如果是,将其放到微任务队列中执行
                if (options.flush === 'post') {
                    const p = Promise.resolve()
                    p.then(job)
                } else {
                    job()
                }
            }
        }
    )
    if (options.immediate) {
        // 当immediate 为true的时候立即执行job,从而触发回调函数
        job()
    } else {
        // 手动调用副作用函数,拿到的值就是旧值
        oldValue = effectFn()
    }
    
}

// 递归读取值
function traverse(value, seen = new Set()) {
    //  如果要读取的是原始值,或者已经被读取过了,那么什么都不做
    if (typeof value !== 'object' || value === 'null' || seen.has(value)) {
        return
    }
    // 将数据添加到seen中,代表遍历地读取过了,避免循环引用的死循环
    seen.add(value)
    // 暂时不考虑数组等其他结构
    // 假设value就是一个对象,使用 for...in读取对象的每一个值,并递归地调用traverse进行处理
    for (const key in value) {
        traverse(value[key], seen)
    }
    // 暂时的,这个返回value无用
    return value
}

function cleanup(effectFn) {
    // 遍历 effectFn.deps数组
    for (let i = 0; i < effectFn.deps.length; i++) {
        // deps 是依赖集合
        const deps = effectFn.deps[i];
        // 将effectFn 从依赖集合中移除
        deps.delete(effectFn)
    }
    // 最后需要重置effectFn.deps 数组
    effectFn.deps.length = 0
}

// 执行区

const obj = reactive({ foo: 1, bar: 2 })

const newObj = proxyRefs({ ...toRefs(obj)})

console.log(newObj.foo)
console.log(newObj.bar)


</script>
</html>

解决了BUG后的代码

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>

<script>
// 定义区
let activeEffect
const effectStack = []

const reactiveMap = new Map()

const arrayInstrumentations = {}

function ref(val) {
    // 在ref函数内部创建一个包裹对象
    const wrapper = {
        value: val     
    }
    // 使用Object.defineProperty在wrapper上定义一个不可枚举的__v_isRef属性,并且值为true
    Object.defineProperty(wrapper, '__v_isRef', {
        value: true
    })
    // 将包裹对象变成响应式数据
    return reactive(wrapper)
}

function toRef(obj, key) {
    const wrapper = {
        get value() {
            return obj[key]
        }
    }
    return wrapper
}

function toRefs(obj) {
    const ret = {}
    for(const key in obj) {
        ret[key] = toRef(obj, key)
    }
    return ret
}

function proxyRefs(target) {
    return new Proxy(target, {
        get(target, key, receiver) {
            const value = Reflect.get(target, key, receiver)
            // 自动脱ref实现:如果读取的值是ref,则返回他的value属性值
            return value.__v_isRef ? value.value : value
        },
        set(target, key, newValue, receiver) {
            // 通过target读取真实值
            const value = target[key]
            // 如果值是ref,则设置其对应的value属性值
            if (value.__v_isRef) {
                value.value = newValue
                return true
            }
            return Reflect.set(target, key, newValue, receiver)
        }
    })
}

;['includes', 'indexOf', 'lastIndexOf'].forEach(method => {
    const originMethod = Array.prototype[method]
    arrayInstrumentations[method] = function(...args) {
        // this时代理对象,现在代理对象中查找,将结果存储到res中
        let res = originMethod.apply(this, args)

        if (res === false || res === -1) {
            // res 为false说明没找到,通过this.raw拿到原始数组,再去其中查找并更新res值
            res = originMethod.apply(this.raw, args)
        }
        return res
    }
})

// 一个标记变量,代表是否进行追踪。默认值为true,即允许追踪
let shouldTrack = true
// 重写数组的push方法
;['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {
    // 取得原始push方法
    const originMethod = Array.prototype[method]
    // 重写
    arrayInstrumentations[method] = function(...args) {
        // 在调用原始方法之前,禁止追踪
        shouldTrack = false
        // push方法的默认行为
        let res = originMethod.apply(this, args)
        // 在调用原始方法之后,恢复原来的行为,即允许追踪
        shouldTrack = true
        return res
    }
})

// 抽离为独立的函数,便于复用
function iterationMethod() {
    // 获取原始数据对象 target
    const target = this.raw
    // 获取原始迭代器方法
    const itr = target[Symbol.iterator] ()

    const wrap = (val) => typeof val === 'object' && val !== null ? reactive(val) : val

    // 调用track函数建立响应联系
    track(target, ITERATE_KEY)

    // 返回自定义的迭代器
    return {
        next() {
            // 调用原始迭代器的nextfangfa获取value和done
            const { value, done } = itr.next()
            return {
                // 如果value不是undefined,则对其进行包裹
                value: value ? [wrap(value[0]), wrap(value[1])] : value,
                done
            }
        },
        // 并且实现可迭代协议
        [Symbol.iterator] () {
            return this
        }
    }
}

function valuesIterationMethod() {
    // 获取原始数据对象
    const target = this.raw
    // 通过target.values获取原始迭代器方法
    const itr = target.values()

    const wrap = (val) => typeof val === 'object' ? reactive(val) : val

    track(target, ITERATE_KEY)

    // 将其返回
    return {
        next() {
            const { value, done } = itr.next()
            return {
                // value是值,而非键值对
                value: wrap(value),
                done
            }
        },
        [Symbol.iterator] () {
            return this
        }
    }
}

const MAP_KEY_ITERATE_KEY = Symbol()

function keysIterationMethod() {
    // 获取原始数据对象
    const target = this.raw
    // 通过target.keys获取原始迭代器方法
    const itr = target.keys()

    const wrap = (val) => typeof val === 'object' ? reactive(val) : val

    track(target, MAP_KEY_ITERATE_KEY)

    // 将其返回
    return {
        next() {
            const { value, done } = itr.next()
            return {
                // value是值,而非键值对
                value: wrap(value),
                done
            }
        },
        [Symbol.iterator] () {
            return this
        }
    }
}

const mutableInstrumentations = {
    add(key) {
        // this 仍然指向的是代理对象,通过raw属性获取原始数据对象
        const target = this.raw
        // 先判断值是否已经存在
        const hadKey = target.has(key)

        // 通过原始数据对象执行add方法添加具体的值
        // 注意,这里不再需要.bind了,因为是直接通过targeet调用并执行的
        const res = target.add(key)
        if (!hadKey) {
            // 通过调用trigger函数触发响应,并指定操作类型为ADD
            trigger(target, key, 'ADD')
        }
        return res
    },

    delete(key) {
        const target = this.raw
        const hadKey = target.has(key)
        const res = target.delete(key)
        if (hadKey) {
            trigger(target, key, 'DELETE')
        }
        return res
    },

    get(key) {
        // 获取原始对象
        const target = this.raw
        // 判断读取的key是否存在
        const had = target.has(key)
        // 追踪依赖,建立响应联系
        track(target, key)
        // 如果存在,则返回结果。这里要注意的是,如果得到的结果res仍然是可代理的数据
        // 则要用reactive返回包装后的响应式数据
        if (had) {
            const res = target.get(key)
            return typeof res === 'object' ? reactive(res) : res
        }
    },

    set(key, value) {
        const target = this.raw
        const had = target.has(key)
        const oldValue = target.get(key)

        // 获取原始数据,由于value本身可能已经是原始数据,所以此时value.raw不存在,则直接使用value
        const rawValue = value.raw || value
        
        target.set(key, rawValue)
        if (!had) {
            trigger(target, key, 'ADD')
        } else if (oldValue !== value || (oldValue === oldValue && value === value)) {
            trigger(target, key, 'SET')
        }
    },

    forEach(callback, thisArg) {
        // wrap函数用来把可代理的值转换为响应式数据
        const wrap = (val) => typeof val === 'object' ? reactive(val) : val
        // 取得原始数据对象
        const target = this.raw
        // 与ITERATE_KEY建立响应联系
        track(target, ITERATE_KEY)
        // 通过原始数据对象调用forEach方法
        target.forEach((v, k) => {
            // 手动调用callback,用wrap方法包裹value和key后在传给callback,这样实现深相应
            callback.call(thisArg, wrap(v), wrap(k), this)
        })
    },

    [Symbol.iterator]: iterationMethod,
    
    entries: iterationMethod,

    values: valuesIterationMethod,

    keys: keysIterationMethod
}

const bucket = new WeakMap()

// 定义一个任务队列
const jobQueue = new Set()
// 使用Promise.resolve() 创建一个promise实例,我们用它将一个任务添加到微任务队列
const p = Promise.resolve()

// 用一个标志代表是否正在刷新队列
let isFlushing = false

const ITERATE_KEY = Symbol()

function flushJob() {
    // 如果任务正在刷新,则什么都不做
    if (isFlushing) return
    // 设置为true,代表正在刷新
    isFlushing = true
    // 在微任务队列刷新jobQueue队列
    p.then(() => {
        jobQueue.forEach(job => job())
    }).finally(() => {
        // 结束后重置isFlushing
        isFlushing = false
    })
}

// 封装 createReactive函数,接受参数isShallow,代表是否为浅相应,默认为false,即非浅响应(深相应)
// 接受参数isReadonly 代表是否只读,默认为false,表示非只读,即可修改
function createReactive(obj, isShallow = false, isReadonly = false) {
    return new Proxy(obj, {
        // 拦截读取操作,接受第三个参数receiver
        get(target, key, receiver) {

            // 如果读取的是raw属性,则返回原始数据对象target
            if (key === 'raw') {
                return target
            }

            if (key === 'size') {
                // 如果读取的是size属性
                // 调用track函数建立响应联系
                track(target, ITERATE_KEY)
                // 通过指定第三个参数receiver为原始对象target从而修复问题
                return Reflect.get(target, key, target)
            }
            // 读取其他属性的默认行为
            // 如果操作的目标对象时数组,并且key存在于arrayInstrumentations上
            // 那么返回定义在arrayInstrumentations上的值
            if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
                return Reflect.get(arrayInstrumentations, key, receiver)
            }

            // 非只读的时候才需要建立响应联系。添加判断,如果key的类型是symbol,则不进行追踪
            if (!isReadonly && typeof key !== 'symbol') {
                track(target, key)
            }

            // 使用reflect.get返回读取到的属性值
            // 当读取属性值时,直接返回了结果,这不能实现深相应。
            // return Reflect.get(target, key, receiver)
            // 得到原始值结果
            const res = Reflect.get(target, key, receiver)

            // 如果配置了浅响应,在这里直接返回原始值
            if (isShallow) {
                return res
            }

            if (typeof res === 'object' && res !== null) {
                // 在这里,如果数据为只读,则调用readonly对值进行包装
                return isReadonly ? readonly(res) : reactive(res)
            }

            // 打补丁,这个是从之前来的,如果不是特殊数据类型,就在这里返回res
            if (Object.prototype.toString.call(target) !== '[object Map]' || Object.prototype.toString.call(target) === '[object Set]') {
                return res
            }

            // 将方法与原始数据对象target绑定后返回
            return mutableInstrumentations[key]
        }, 

        has(target, key) {
            track(target, key)
            return Reflect.has(target, key)
        },

        ownKeys(target) {
            // 如果操作目标target是数组,则使用length属性作为key并建立响应联系
            // 将副作用函数与ITERATE_KEY 关联
            Array.isArray(target) ? track(target, 'length') : track(target, ITERATE_KEY)
            return Reflect.ownKeys(target)
        },

        set(target, key, newValue, receiver) {
            // 拦截设置操作

            // 如果数据是只读的,打印警告信息,直接返回
            if (isReadonly) {
                console.warn(`属性 ${key} 是只读的`)
                return
            }

            // 先获取旧值
            const oldValue = target[key]

            // 如果属性不存在,则说明是添加新属性,否则时设置已有属性
            // type可能是数组或者对象
            // 数组:如果设置的索引值小于长度,那么是SET操作,否则是ADD操作
            const type = Array.isArray(target) ? Number(key) < target.length ? 'SET' : 'ADD' :
            Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
            // 设置属性值
            const res = Reflect.set(target, key, newValue, receiver)

            // target === receiver.raw 说明receiver就是target的代理对象
            if (target === receiver.raw) {
                // 比较新值和旧值,只要当不全等的时候才触发相应
                if (oldValue !== newValue) {
                    // 将type作为第三个参数传递给trigger函数
                    // target[key] = newValue // 使用了Reflect.set设置,不必使用此句
                    // 增加第四个参数,即触发响应的新值
                    trigger(target, key, type, newValue)
                }
            }
            
            return res
            
        },

        deleteProperty(target, key) {

            // 如果数据是只读的,打印警告信息,直接返回
            if (isReadonly) {
                console.warn(`属性 ${key} 是只读的`)
                return
            }

            // 检查被操作的属性是否时对象自己的属性
            const hadKey = Object.prototype.hasOwnProperty.call(target, key)
            
            // 使用Reflect.deleteProperty完成属性的删除
            const res = Reflect.deleteProperty(target, key)
            
            if (res && hadKey) {
                // 只有当被删除的属性是对象自己的属性并且成功删除时,才触发更新
                trigger(target, key, 'DELETE')
            }
            return res
        }
    })
}

function reactive(obj) {
    // 优先通过原始对象obj寻找之前创建的代理对象,如果找到了,直接返回已有的代理对象
    const existionProxy = reactiveMap.get(obj)
    if (existionProxy) {
        return existionProxy
    }
    // 否则,创建新的代理对象
    const proxy = createReactive(obj)
    // 存储到map中,从而米便重复创建
    reactiveMap.set(obj, proxy)
    return proxy
}

function shallowReactive(obj) {
    return createReactive(obj, true)
}

function readonly(obj) {
    return createReactive(obj, false, true)
}

function shallowReadonly(obj) {
    return createReactive(obj, true, true)
}

function track(target, key) {
    // 没有副作用 直接返回
    // 当禁止追踪时,直接返回
    if (!activeEffect || !shouldTrack) return
    
    let depsMap = bucket.get(target)
    if (!depsMap) bucket.set(target, depsMap = new Map())
    
    // dep 预期是text1
    let deps = depsMap.get(key)
    if (!deps) depsMap.set(key, deps = new Set())
    
    // 这样,修改某一属性就详细到代理的某一个对象的某个属性
    deps.add(activeEffect)
    // deps 就是一个与当前副作用函数存在联系的依赖集合
    activeEffect.deps.push(deps)
}

function trigger(target, key, type, newValue) {
    // 设置的时候 通知对应的修改函数
    let depsMap = bucket.get(target)
    if (!depsMap) return
    // 取得与key相关联的副作用函数
    let effects = depsMap.get(key)
    

    const effectsToRun = new Set()
    // 将与key相关联的副作用函数添加到effectsToRun
    effects && effects.forEach(effectFn => {
        if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
        }
    })
    // 只有当操作类型为ADD 或者 DELETE时,才触发与ITERATE_KEY相关联的副作用函数重新执行
    // 如果操作类型是SET,并且目标对象是Map类型的数据,也应该触发那些与ITERATE_KEY相关联的副作用函数重新执行
    if (type === 'ADD' || type === 'DELETE' || (type === 'SET' && Object.prototype.toString.call(target) === '[object Map]')) {
        // 取得与ITERATE_KEY相关联的副作用函数
        const iterateEffects = depsMap.get(ITERATE_KEY)
        // 将与ITERATE_KEY相关联的副作用函数也添加到effectsToRun
        iterateEffects && iterateEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }
    
    if (type === 'ADD' || type === 'DELETE' && (type === 'SET' && Object.prototype.toString.call(target) === '[object Map]')) {
        // 将与MAP_KEY_ITERATE_KEY相关联的副作用函数也添加到effectsToRun
        const iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY)
        iterateEffects && iterateEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }

    // 当操作类型为ADD并且目标对象是数组时,应该取出并执行属性相关联的副作用函数
    if (type === 'ADD' && Array.isArray(target)) {
        // 取出与length相关联的副作用函数
        const lengthEffects = depsMap.get('length')
        lengthEffects && lengthEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }

    // 如果操作目标是数组,并且修改了数组的length属性
    if (Array.isArray(target) && key === 'length') {
        // 对于索引大于或者等于新的length值的元素
        depsMap.forEach((effects, key) => {
            if (key >= newValue) {
                effects.forEach(effectFn => {
                    if (effectFn !== activeEffect) {
                        effectsToRun.add(effectFn)
                    }
                })
            }
        })
    }
    

    effectsToRun.forEach(item => {
        // 如果一个副作用函数存在调度器,则调用该调度器,并将副作用函数作为参数传递
        if (item.options.scheduler) {
            item.options.scheduler(item.func)
        } else {
            item.func()
        }
    })
}

function effect(fn, options = {}) {
    // 这个effectFn是副作用函数
    const effectFn = {
        func: () => {
            cleanup(effectFn)
            // 当调用effect注册副作用函数时,将副作用函数赋值给activeEffect
            activeEffect = effectFn
            // 在调用副作用函数之前将当前副作用函数压入栈中
            effectStack.push(effectFn)
            // 将fn的执行结果存储到res中
            const res = fn()
            // 在调用副作用函数之后,将当前副作用函数弹出栈,并把activeEffect还原成之前的值
            effectStack.pop()
            activeEffect = effectStack[effectStack.length - 1]
            return res
        }
    }
    // 将options挂载到effectFn上
    effectFn.options = options
    // activeEffect.deps用来存储所有与该副作用函数相关联的依赖集合
    effectFn.deps = []
    if (!options.lazy) {
        effectFn.func()
    }
    return effectFn.func
}

function computed(getter) {
    // 使用value来缓存上一次计算的值
    let value
    // dirty标志,用来标识是否需要重新计算值,为true代表脏,需要重新计算
    let dirty = true
    // 把getter作为副作用函数,创建一个lazy的effect
    const effectFn = effect(getter, {
        lazy: true,
        // 添加调度器,在调度器中将dirty重置为true
        scheduler() {
            // 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发相应
            if (!dirty) {
                dirty = true
                trigger(obj, 'value')    
            }
            
        }
    })
    const obj = {
        // 当读取到value才执行effectFn
        get value() {
            if (dirty) {
                value = effectFn()
                // 将dirty设置为false,下一次访问直接使用缓存到value的值
                dirty = false
            }
            // 当读取value时,手动调用track函数进行追踪
            track(obj, 'value')
            return value
        }
    }
    return obj
}

// watch 函数接收两个参数,source是响应式数据,cb是回调函数,当监控到source数据变化时,cb执行
function watch(source, cb, options = {}) {
    // 定义getter
    let getter
    // 如果source是函数,说明用户传递的是getter,所以直接把source赋值给getter
    if (typeof source === 'function') {
        getter = source
    } else {
        getter = () => traverse(source)
    }
    // 定义旧值和新值
    let oldValue, newValue
    // 使用effect注册副作用函数时,开启lazy选项,并把返回值存储到effectFn中以便后续手动调用
    
    // cleanup用来存储用户注册的过期回调
    let cleanup
    // 定义onInvalidate函数
    function onInvalidate(fn) {
        cleanup = fn
    }


    // 提取scheduler调度函数为一个独立的job函数
    const job = () => {
        // 在scheduler中重新执行副作用函数,得到的是新值
        newValue = effectFn()
        // 在调用回调函数cb之前,先调用过期回调
        if (cleanup) {
            cleanup()
        }

        // 当数据变化时,调用回调函数cb
        // 将旧值和新值作为回调函数的参数
        // 将onInvalidate作为回调函数的第三个参数,以便用户使用
        cb(newValue, oldValue, onInvalidate)
        // 更新旧值,不然下一次会得到错误的旧值
        oldValue = newValue
    }

    const effectFn = effect(
        // 触发读取操作,从而建立联系
        () => getter(),
        {
            lazy: true,
            // 使用job函数作为调度器函数
            scheduler: () => {
                // 在调度函数中判断flush是否为post,如果是,将其放到微任务队列中执行
                if (options.flush === 'post') {
                    const p = Promise.resolve()
                    p.then(job)
                } else {
                    job()
                }
            }
        }
    )
    if (options.immediate) {
        // 当immediate 为true的时候立即执行job,从而触发回调函数
        job()
    } else {
        // 手动调用副作用函数,拿到的值就是旧值
        oldValue = effectFn()
    }
    
}

// 递归读取值
function traverse(value, seen = new Set()) {
    //  如果要读取的是原始值,或者已经被读取过了,那么什么都不做
    if (typeof value !== 'object' || value === 'null' || seen.has(value)) {
        return
    }
    // 将数据添加到seen中,代表遍历地读取过了,避免循环引用的死循环
    seen.add(value)
    // 暂时不考虑数组等其他结构
    // 假设value就是一个对象,使用 for...in读取对象的每一个值,并递归地调用traverse进行处理
    for (const key in value) {
        traverse(value[key], seen)
    }
    // 暂时的,这个返回value无用
    return value
}

function cleanup(effectFn) {
    // 遍历 effectFn.deps数组
    for (let i = 0; i < effectFn.deps.length; i++) {
        // deps 是依赖集合
        const deps = effectFn.deps[i];
        // 将effectFn 从依赖集合中移除
        deps.delete(effectFn)
    }
    // 最后需要重置effectFn.deps 数组
    effectFn.deps.length = 0
}

// 执行区

const obj = reactive({ foo: 1, bar: 2 })

const newObj = proxyRefs({ ...toRefs(obj)})

console.log(newObj.foo)
console.log(newObj.bar)

</script>
</html>

解决新bug

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>

<script>
// 定义区
let activeEffect
const effectStack = []

const reactiveMap = new Map()

const arrayInstrumentations = {}

function ref(val) {
    // 在ref函数内部创建一个包裹对象
    const wrapper = {
        value: val     
    }
    // 使用Object.defineProperty在wrapper上定义一个不可枚举的__v_isRef属性,并且值为true
    Object.defineProperty(wrapper, '__v_isRef', {
        value: true
    })
    // 将包裹对象变成响应式数据
    return reactive(wrapper)
}

function toRef(obj, key) {
    const wrapper = {
        get value() {
            return obj[key]
        },
        set value(val) {
            obj[key] = val
        }
    }

    // 使用Object.defineProperty在wrapper上定义一个不可枚举的__v_isRef属性,并且值为true
    Object.defineProperty(wrapper, '__v_isRef', {
        value: true
    })
    // 将包裹对象变成响应式数据
    return wrapper
}

function toRefs(obj) {
    const ret = {}
    for(const key in obj) {
        ret[key] = toRef(obj, key)
    }
    return ret
}

function proxyRefs(target) {
    return new Proxy(target, {
        get(target, key, receiver) {
            const value = Reflect.get(target, key, receiver)
            // 自动脱ref实现:如果读取的值是ref,则返回他的value属性值
            return value.__v_isRef ? value.value : value
        },
        set(target, key, newValue, receiver) {
            // 通过target读取真实值
            const value = target[key]
            // 如果值是ref,则设置其对应的value属性值
            if (value.__v_isRef) {
                value.value = newValue
                return true
            }
            return Reflect.set(target, key, newValue, receiver)
        }
    })
}

;['includes', 'indexOf', 'lastIndexOf'].forEach(method => {
    const originMethod = Array.prototype[method]
    arrayInstrumentations[method] = function(...args) {
        // this时代理对象,现在代理对象中查找,将结果存储到res中
        let res = originMethod.apply(this, args)

        if (res === false || res === -1) {
            // res 为false说明没找到,通过this.raw拿到原始数组,再去其中查找并更新res值
            res = originMethod.apply(this.raw, args)
        }
        return res
    }
})

// 一个标记变量,代表是否进行追踪。默认值为true,即允许追踪
let shouldTrack = true
// 重写数组的push方法
;['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {
    // 取得原始push方法
    const originMethod = Array.prototype[method]
    // 重写
    arrayInstrumentations[method] = function(...args) {
        // 在调用原始方法之前,禁止追踪
        shouldTrack = false
        // push方法的默认行为
        let res = originMethod.apply(this, args)
        // 在调用原始方法之后,恢复原来的行为,即允许追踪
        shouldTrack = true
        return res
    }
})

// 抽离为独立的函数,便于复用
function iterationMethod() {
    // 获取原始数据对象 target
    const target = this.raw
    // 获取原始迭代器方法
    const itr = target[Symbol.iterator] ()

    const wrap = (val) => typeof val === 'object' && val !== null ? reactive(val) : val

    // 调用track函数建立响应联系
    track(target, ITERATE_KEY)

    // 返回自定义的迭代器
    return {
        next() {
            // 调用原始迭代器的nextfangfa获取value和done
            const { value, done } = itr.next()
            return {
                // 如果value不是undefined,则对其进行包裹
                value: value ? [wrap(value[0]), wrap(value[1])] : value,
                done
            }
        },
        // 并且实现可迭代协议
        [Symbol.iterator] () {
            return this
        }
    }
}

function valuesIterationMethod() {
    // 获取原始数据对象
    const target = this.raw
    // 通过target.values获取原始迭代器方法
    const itr = target.values()

    const wrap = (val) => typeof val === 'object' ? reactive(val) : val

    track(target, ITERATE_KEY)

    // 将其返回
    return {
        next() {
            const { value, done } = itr.next()
            return {
                // value是值,而非键值对
                value: wrap(value),
                done
            }
        },
        [Symbol.iterator] () {
            return this
        }
    }
}

const MAP_KEY_ITERATE_KEY = Symbol()

function keysIterationMethod() {
    // 获取原始数据对象
    const target = this.raw
    // 通过target.keys获取原始迭代器方法
    const itr = target.keys()

    const wrap = (val) => typeof val === 'object' ? reactive(val) : val

    track(target, MAP_KEY_ITERATE_KEY)

    // 将其返回
    return {
        next() {
            const { value, done } = itr.next()
            return {
                // value是值,而非键值对
                value: wrap(value),
                done
            }
        },
        [Symbol.iterator] () {
            return this
        }
    }
}

const mutableInstrumentations = {
    add(key) {
        // this 仍然指向的是代理对象,通过raw属性获取原始数据对象
        const target = this.raw
        // 先判断值是否已经存在
        const hadKey = target.has(key)

        // 通过原始数据对象执行add方法添加具体的值
        // 注意,这里不再需要.bind了,因为是直接通过targeet调用并执行的
        const res = target.add(key)
        if (!hadKey) {
            // 通过调用trigger函数触发响应,并指定操作类型为ADD
            trigger(target, key, 'ADD')
        }
        return res
    },

    delete(key) {
        const target = this.raw
        const hadKey = target.has(key)
        const res = target.delete(key)
        if (hadKey) {
            trigger(target, key, 'DELETE')
        }
        return res
    },

    get(key) {
        // 获取原始对象
        const target = this.raw
        // 判断读取的key是否存在
        const had = target.has(key)
        // 追踪依赖,建立响应联系
        track(target, key)
        // 如果存在,则返回结果。这里要注意的是,如果得到的结果res仍然是可代理的数据
        // 则要用reactive返回包装后的响应式数据
        if (had) {
            const res = target.get(key)
            return typeof res === 'object' ? reactive(res) : res
        }
    },

    set(key, value) {
        const target = this.raw
        const had = target.has(key)
        const oldValue = target.get(key)

        // 获取原始数据,由于value本身可能已经是原始数据,所以此时value.raw不存在,则直接使用value
        const rawValue = value.raw || value
        
        target.set(key, rawValue)
        if (!had) {
            trigger(target, key, 'ADD')
        } else if (oldValue !== value || (oldValue === oldValue && value === value)) {
            trigger(target, key, 'SET')
        }
    },

    forEach(callback, thisArg) {
        // wrap函数用来把可代理的值转换为响应式数据
        const wrap = (val) => typeof val === 'object' ? reactive(val) : val
        // 取得原始数据对象
        const target = this.raw
        // 与ITERATE_KEY建立响应联系
        track(target, ITERATE_KEY)
        // 通过原始数据对象调用forEach方法
        target.forEach((v, k) => {
            // 手动调用callback,用wrap方法包裹value和key后在传给callback,这样实现深相应
            callback.call(thisArg, wrap(v), wrap(k), this)
        })
    },

    [Symbol.iterator]: iterationMethod,
    
    entries: iterationMethod,

    values: valuesIterationMethod,

    keys: keysIterationMethod
}

const bucket = new WeakMap()

// 定义一个任务队列
const jobQueue = new Set()
// 使用Promise.resolve() 创建一个promise实例,我们用它将一个任务添加到微任务队列
const p = Promise.resolve()

// 用一个标志代表是否正在刷新队列
let isFlushing = false

const ITERATE_KEY = Symbol()

function flushJob() {
    // 如果任务正在刷新,则什么都不做
    if (isFlushing) return
    // 设置为true,代表正在刷新
    isFlushing = true
    // 在微任务队列刷新jobQueue队列
    p.then(() => {
        jobQueue.forEach(job => job())
    }).finally(() => {
        // 结束后重置isFlushing
        isFlushing = false
    })
}

// 封装 createReactive函数,接受参数isShallow,代表是否为浅相应,默认为false,即非浅响应(深相应)
// 接受参数isReadonly 代表是否只读,默认为false,表示非只读,即可修改
function createReactive(obj, isShallow = false, isReadonly = false) {
    return new Proxy(obj, {
        // 拦截读取操作,接受第三个参数receiver
        get(target, key, receiver) {

            // 如果读取的是raw属性,则返回原始数据对象target
            if (key === 'raw') {
                return target
            }

            if (key === 'size') {
                // 如果读取的是size属性
                // 调用track函数建立响应联系
                track(target, ITERATE_KEY)
                // 通过指定第三个参数receiver为原始对象target从而修复问题
                return Reflect.get(target, key, target)
            }
            // 读取其他属性的默认行为
            // 如果操作的目标对象时数组,并且key存在于arrayInstrumentations上
            // 那么返回定义在arrayInstrumentations上的值
            if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
                return Reflect.get(arrayInstrumentations, key, receiver)
            }

            // 非只读的时候才需要建立响应联系。添加判断,如果key的类型是symbol,则不进行追踪
            if (!isReadonly && typeof key !== 'symbol') {
                track(target, key)
            }

            // 使用reflect.get返回读取到的属性值
            // 当读取属性值时,直接返回了结果,这不能实现深相应。
            // return Reflect.get(target, key, receiver)
            // 得到原始值结果
            const res = Reflect.get(target, key, receiver)

            // 如果配置了浅响应,在这里直接返回原始值
            if (isShallow) {
                return res
            }

            if (typeof res === 'object' && res !== null) {
                // 在这里,如果数据为只读,则调用readonly对值进行包装
                return isReadonly ? readonly(res) : reactive(res)
            }

            // 打补丁,这个是从之前来的,如果不是特殊数据类型,就在这里返回res
            if (Object.prototype.toString.call(target) !== '[object Map]' && Object.prototype.toString.call(target) !== '[object Set]') {
                return res
            }

            // 将方法与原始数据对象target绑定后返回
            return mutableInstrumentations[key]
        }, 

        has(target, key) {
            track(target, key)
            return Reflect.has(target, key)
        },

        ownKeys(target) {
            // 如果操作目标target是数组,则使用length属性作为key并建立响应联系
            // 将副作用函数与ITERATE_KEY 关联
            Array.isArray(target) ? track(target, 'length') : track(target, ITERATE_KEY)
            return Reflect.ownKeys(target)
        },

        set(target, key, newValue, receiver) {
            // 拦截设置操作

            // 如果数据是只读的,打印警告信息,直接返回
            if (isReadonly) {
                console.warn(`属性 ${key} 是只读的`)
                return
            }

            // 先获取旧值
            const oldValue = target[key]

            // 如果属性不存在,则说明是添加新属性,否则时设置已有属性
            // type可能是数组或者对象
            // 数组:如果设置的索引值小于长度,那么是SET操作,否则是ADD操作
            const type = Array.isArray(target) ? Number(key) < target.length ? 'SET' : 'ADD' :
            Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
            // 设置属性值
            const res = Reflect.set(target, key, newValue, receiver)

            // target === receiver.raw 说明receiver就是target的代理对象
            if (target === receiver.raw) {
                // 比较新值和旧值,只要当不全等的时候才触发相应
                if (oldValue !== newValue) {
                    // 将type作为第三个参数传递给trigger函数
                    // target[key] = newValue // 使用了Reflect.set设置,不必使用此句
                    // 增加第四个参数,即触发响应的新值
                    trigger(target, key, type, newValue)
                }
            }
            
            return res
            
        },

        deleteProperty(target, key) {

            // 如果数据是只读的,打印警告信息,直接返回
            if (isReadonly) {
                console.warn(`属性 ${key} 是只读的`)
                return
            }

            // 检查被操作的属性是否时对象自己的属性
            const hadKey = Object.prototype.hasOwnProperty.call(target, key)
            
            // 使用Reflect.deleteProperty完成属性的删除
            const res = Reflect.deleteProperty(target, key)
            
            if (res && hadKey) {
                // 只有当被删除的属性是对象自己的属性并且成功删除时,才触发更新
                trigger(target, key, 'DELETE')
            }
            return res
        }
    })
}

function reactive(obj) {
    // 优先通过原始对象obj寻找之前创建的代理对象,如果找到了,直接返回已有的代理对象
    const existionProxy = reactiveMap.get(obj)
    if (existionProxy) {
        return existionProxy
    }
    // 否则,创建新的代理对象
    const proxy = createReactive(obj)
    // 存储到map中,从而米便重复创建
    reactiveMap.set(obj, proxy)
    return proxy
}

function shallowReactive(obj) {
    return createReactive(obj, true)
}

function readonly(obj) {
    return createReactive(obj, false, true)
}

function shallowReadonly(obj) {
    return createReactive(obj, true, true)
}

function track(target, key) {
    // 没有副作用 直接返回
    // 当禁止追踪时,直接返回
    if (!activeEffect || !shouldTrack) return
    
    let depsMap = bucket.get(target)
    if (!depsMap) bucket.set(target, depsMap = new Map())
    
    // dep 预期是text1
    let deps = depsMap.get(key)
    if (!deps) depsMap.set(key, deps = new Set())
    
    // 这样,修改某一属性就详细到代理的某一个对象的某个属性
    deps.add(activeEffect)
    // deps 就是一个与当前副作用函数存在联系的依赖集合
    activeEffect.deps.push(deps)
}

function trigger(target, key, type, newValue) {
    // 设置的时候 通知对应的修改函数
    let depsMap = bucket.get(target)
    if (!depsMap) return
    // 取得与key相关联的副作用函数
    let effects = depsMap.get(key)
    

    const effectsToRun = new Set()
    // 将与key相关联的副作用函数添加到effectsToRun
    effects && effects.forEach(effectFn => {
        if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
        }
    })
    // 只有当操作类型为ADD 或者 DELETE时,才触发与ITERATE_KEY相关联的副作用函数重新执行
    // 如果操作类型是SET,并且目标对象是Map类型的数据,也应该触发那些与ITERATE_KEY相关联的副作用函数重新执行
    if (type === 'ADD' || type === 'DELETE' || (type === 'SET' && Object.prototype.toString.call(target) === '[object Map]')) {
        // 取得与ITERATE_KEY相关联的副作用函数
        const iterateEffects = depsMap.get(ITERATE_KEY)
        // 将与ITERATE_KEY相关联的副作用函数也添加到effectsToRun
        iterateEffects && iterateEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }
    
    if (type === 'ADD' || type === 'DELETE' && (type === 'SET' && Object.prototype.toString.call(target) === '[object Map]')) {
        // 将与MAP_KEY_ITERATE_KEY相关联的副作用函数也添加到effectsToRun
        const iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY)
        iterateEffects && iterateEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }

    // 当操作类型为ADD并且目标对象是数组时,应该取出并执行属性相关联的副作用函数
    if (type === 'ADD' && Array.isArray(target)) {
        // 取出与length相关联的副作用函数
        const lengthEffects = depsMap.get('length')
        lengthEffects && lengthEffects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
                effectsToRun.add(effectFn)
            }
        })
    }

    // 如果操作目标是数组,并且修改了数组的length属性
    if (Array.isArray(target) && key === 'length') {
        // 对于索引大于或者等于新的length值的元素
        depsMap.forEach((effects, key) => {
            if (key >= newValue) {
                effects.forEach(effectFn => {
                    if (effectFn !== activeEffect) {
                        effectsToRun.add(effectFn)
                    }
                })
            }
        })
    }
    

    effectsToRun.forEach(item => {
        // 如果一个副作用函数存在调度器,则调用该调度器,并将副作用函数作为参数传递
        if (item.options.scheduler) {
            item.options.scheduler(item.func)
        } else {
            item.func()
        }
    })
}

function effect(fn, options = {}) {
    // 这个effectFn是副作用函数
    const effectFn = {
        func: () => {
            cleanup(effectFn)
            // 当调用effect注册副作用函数时,将副作用函数赋值给activeEffect
            activeEffect = effectFn
            // 在调用副作用函数之前将当前副作用函数压入栈中
            effectStack.push(effectFn)
            // 将fn的执行结果存储到res中
            const res = fn()
            // 在调用副作用函数之后,将当前副作用函数弹出栈,并把activeEffect还原成之前的值
            effectStack.pop()
            activeEffect = effectStack[effectStack.length - 1]
            return res
        }
    }
    // 将options挂载到effectFn上
    effectFn.options = options
    // activeEffect.deps用来存储所有与该副作用函数相关联的依赖集合
    effectFn.deps = []
    if (!options.lazy) {
        effectFn.func()
    }
    return effectFn.func
}

function computed(getter) {
    // 使用value来缓存上一次计算的值
    let value
    // dirty标志,用来标识是否需要重新计算值,为true代表脏,需要重新计算
    let dirty = true
    // 把getter作为副作用函数,创建一个lazy的effect
    const effectFn = effect(getter, {
        lazy: true,
        // 添加调度器,在调度器中将dirty重置为true
        scheduler() {
            // 当计算属性依赖的响应式数据变化时,手动调用trigger函数触发相应
            if (!dirty) {
                dirty = true
                trigger(obj, 'value')    
            }
            
        }
    })
    const obj = {
        // 当读取到value才执行effectFn
        get value() {
            if (dirty) {
                value = effectFn()
                // 将dirty设置为false,下一次访问直接使用缓存到value的值
                dirty = false
            }
            // 当读取value时,手动调用track函数进行追踪
            track(obj, 'value')
            return value
        }
    }
    return obj
}

// watch 函数接收两个参数,source是响应式数据,cb是回调函数,当监控到source数据变化时,cb执行
function watch(source, cb, options = {}) {
    // 定义getter
    let getter
    // 如果source是函数,说明用户传递的是getter,所以直接把source赋值给getter
    if (typeof source === 'function') {
        getter = source
    } else {
        getter = () => traverse(source)
    }
    // 定义旧值和新值
    let oldValue, newValue
    // 使用effect注册副作用函数时,开启lazy选项,并把返回值存储到effectFn中以便后续手动调用
    
    // cleanup用来存储用户注册的过期回调
    let cleanup
    // 定义onInvalidate函数
    function onInvalidate(fn) {
        cleanup = fn
    }


    // 提取scheduler调度函数为一个独立的job函数
    const job = () => {
        // 在scheduler中重新执行副作用函数,得到的是新值
        newValue = effectFn()
        // 在调用回调函数cb之前,先调用过期回调
        if (cleanup) {
            cleanup()
        }

        // 当数据变化时,调用回调函数cb
        // 将旧值和新值作为回调函数的参数
        // 将onInvalidate作为回调函数的第三个参数,以便用户使用
        cb(newValue, oldValue, onInvalidate)
        // 更新旧值,不然下一次会得到错误的旧值
        oldValue = newValue
    }

    const effectFn = effect(
        // 触发读取操作,从而建立联系
        () => getter(),
        {
            lazy: true,
            // 使用job函数作为调度器函数
            scheduler: () => {
                // 在调度函数中判断flush是否为post,如果是,将其放到微任务队列中执行
                if (options.flush === 'post') {
                    const p = Promise.resolve()
                    p.then(job)
                } else {
                    job()
                }
            }
        }
    )
    if (options.immediate) {
        // 当immediate 为true的时候立即执行job,从而触发回调函数
        job()
    } else {
        // 手动调用副作用函数,拿到的值就是旧值
        oldValue = effectFn()
    }
    
}

// 递归读取值
function traverse(value, seen = new Set()) {
    //  如果要读取的是原始值,或者已经被读取过了,那么什么都不做
    if (typeof value !== 'object' || value === 'null' || seen.has(value)) {
        return
    }
    // 将数据添加到seen中,代表遍历地读取过了,避免循环引用的死循环
    seen.add(value)
    // 暂时不考虑数组等其他结构
    // 假设value就是一个对象,使用 for...in读取对象的每一个值,并递归地调用traverse进行处理
    for (const key in value) {
        traverse(value[key], seen)
    }
    // 暂时的,这个返回value无用
    return value
}

function cleanup(effectFn) {
    // 遍历 effectFn.deps数组
    for (let i = 0; i < effectFn.deps.length; i++) {
        // deps 是依赖集合
        const deps = effectFn.deps[i];
        // 将effectFn 从依赖集合中移除
        deps.delete(effectFn)
    }
    // 最后需要重置effectFn.deps 数组
    effectFn.deps.length = 0
}

// 执行区

// const obj = reactive({ foo: 1, bar: 2 })

// const refFoo = toRef(obj, 'foo')

// refFoo.value = 100

// console.log(refFoo)

// const newObj = proxyRefs({ ...toRefs(obj)})

// console.log(newObj.foo)
// console.log(newObj.bar)

const count = ref(0)

const countObj = reactive({count})

console.log(countObj.count)

</script>
</html>

reactive自动脱ref的内容在这里

image-20240813150551012

经过验证,可以自动脱ref(这里应该没问题了)。也可以自动穿上(不严谨验证)