第八章 实现readonly功能

文章详细介绍了如何重构Vue.js中的readonly和reactive功能,通过提取共通代码、创建getter和setter函数以及优化性能,如减少函数调用次数,将handler抽离到单独文件,并在readonly中处理set方法的警告。
摘要由CSDN通过智能技术生成

实现readonly功能

readonly是和reactived很类似但是不可以修改 所以就不需要依赖的收集和触发,因此他的代码十分简单

首先还是附上测试代码:

readonly.spec.ts

import { readonly } from "../reactive"

describe('readonly',()=>{
    it('happy path',()=>{
        // not set readonly是和reactived很类似但是不可以修改 所以就不需要依赖的收集和触发
        const original = {foo:1,bar:{baz:2}}
        const wrapped = readonly(original)
        expect(wrapped).not.toBe(original)
        expect(wrapped.foo).toBe(1)
    })
})

reactive.ts

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
        }
    })
}

看了代码后,是不是觉得其实就是把reactive的依赖收集和触发依赖去掉,然后把set功能去掉就没了,确实是这样的。当时这节就这样结束也太短了吧,所以,不存在结束的,长起来。

重构

先来看看我们现在reactive.ts中的代码

image.png

是不是觉得这些get和set的代码非常的相似,做的非常像,没错,这就是我们今天要重构的地方。

首先我们先对get函数进行重构,一开始想着肯定是在外部定义一个function get函数抽离get的逻辑,然后分别给reactive和readonly所以就有了这个get函数

image.png

但是仔细一想其实reactive和readonly的get还是不一样的,所以我们就使用一个createGetter这样的高级函数去将get函数返回出来,然后我们给createGetter这个函数去传入isReadonly,区别reactive和readonly。

image.png

既然get都抽离完了,那就到set了,基本上想法和get一样的,甚至比get简单,因为readonly中没有set功能,所以函数中甚至都不需要判断

image.png

这样也就抽离完毕了,比起一开始reactive和readonly中简洁多了

image.png

但是其实这样看来我们reactive中的代码还是挺多的,这时候我们还是可以再抽离的,我们可以把proxy中的handler函数抽离到baseHandlers文件中去,抽离完之后就很简洁了,就只剩下几行代码了,get set的逻辑都抽到baseHandlers中去了

image.png

这样看我们的优化应该就快到头了,不过,我们还要再来看看性能方面:

image.png

红框中的写法会让我们每调用到new Proxy就会掉一次createGetter/createSetter函数,所以我们其实可以把他抽到外面,让它初始化的时候调用,减少他的调用次数,如下图

image.png

我们还可以new Proxy这个抽离出来,如图

image.png

最后的最后,我们需要给readonly的set方法做一个处理,如果是readonly而且调用了set方法,就给他爆出一个警告,我们先写测试用例:

it('warn then call set',()=>{
        console.warn = jest.fn()

        const user = readonly({
            age:10
        })

        user.age = 11

        expect(console.warn).toBeCalled()
})

这时候我们只需要给readonly的set函数中调用一次console.warn就可以爆出警告通过测试用例

image.png

接下来就给出几个修改文件的代码

reactive.ts

import { mutableHandlers, readonlyHandlers } from "./baseHandlers"

export function reactive(raw){

    return createReactiveObject(raw,mutableHandlers)
}

export function readonly(raw){

    return createReactiveObject(raw,readonlyHandlers)
}

function createReactiveObject(target,baseHandlers){
    return new Proxy(target,baseHandlers)
}

baseHandlers.ts

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

const get = createGetter()
const set = createSetter()
const readonlyGet = createGetter(true)

function createGetter(isReadonly = false){
    return function get(target,key){
        const res = Reflect.get(target,key)

        if(!isReadonly) {
            track(target,key)
        }

        return res
    }
}

function createSetter() {
    return function set(target,key,value){
        const res = Reflect.set(target,key,value)

        trigger(target,key)
        return res
    }
}

export const mutableHandlers = {
    get:get,
    set:set
}

export const readonlyHandlers = {
    get:readonlyGet,
    set(target,key,value){
        console.warn(`key:${key} set 失败 因为 target 是readonly`,target)
        return true
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值