实现vue3源码-readonly

161 阅读2分钟

readonly和reactive的功能非常类似,现在就来实现一下readonly

readonly

优化

结语

readonly

readonly 只读,所以我们不需要触发依赖,自然也就不需要收集依赖了,直接get就行,set抛出警告

export function readonly(raw) {
    return new Proxy(raw, {
        get(target, key) {
            const res = Reflect.get(target, key)
            return res
        },
        set(target, key, value) {
            return true

        }
    })
}

优化

其实这样readonly就已经实现了,但是readonly和reactive代码耦合度过高,可以抽离为公共部分,加以处理就可以了

reactive.ts

import { mutableHandlers, readonlyBaseHandlers } from './baseHandlers'

export function reactive(raw: any) {
    return createActiveObject(raw)
}

export function readonly(raw) {
    return createActiveObject(raw, readonlyBaseHandlers)
}

// 如果抽离了 readonly 的 set,这里默认值设不设置都可以,我这里抽离了的,所以默认值放上是可以的
function createActiveObject(raw, beseHandlers = mutableHandlers) {
    return new Proxy(raw, beseHandlers)
}

这样抽离出来之后,把proxy的handlers单独存放到其他文件就行,看起来也更整洁

这里我在同级单独开个baseHandlers.ts存放一些配置,方法之类的,包括重构之后的优化,注释也有写

我这里readonly和reactive的set也抽离为一个公共代码,其实它俩功能有些区别,所以不抽离更符合语义,我只是闲麻烦才抽离的,观者也可以不抽离

import { track, trigger } from "./effect"

// 不需要每次都创建一个新的get,set,初始化时创建一个即可
const get = createGetter()
const set = createSetter()

const readonlyGet = createGetter(true)
const readonlySet = createSetter(true)

// 抽离get
function createGetter(isReadonly = false) {
    return function get(target, key) {
        const res = Reflect.get(target, key)
        // 依赖收集
        // 抽离公共,判断是否需要收集依赖<readonly>
        !isReadonly && track(target, key)
        return res
    }
}

// 抽离set
// readonly 的 set 和 reactive 的 set 是有功能的本质区别
// 所以可以不抽离,或者单独抽离一个 readonly 的 set <readonlySet>,我这里是抽离了的
function createSetter(isReadonly = false) {
    return function set(target, key, value) {
        const res = Reflect.set(target, key, value)
        if (!isReadonly) {
            // 触发依赖
            trigger(target, key)
            return res
        } else {
            console.warn(`key${key}set失败,${target}是只读`)
            return true
        }
    }
}

// 不需要每次都创建一个新的get,set,初始化时创建一个即可
export const mutableHandlers = {
    get,
    set
}

export const readonlyBaseHandlers = {
    get: readonlyGet,
    set: readonlySet
}

结语

这里优化使用了高阶函数,初始化仅创建一次得思路,抽离公共区域,也比较有参考意义,感谢