【Vue3源码】第三章 readonly详解 从零实现Vue3 readonly API

【Vue3源码】第三章 readonly详解 从零实现Vue3 readonly API

前言

上一章节我们实现了effect函数的stop和onstop功能,至此effect函数源码的编写就暂时告一段落了,这一章我们继续解读Vue3源码,开始实现Vue3 Reactivity :core 中的readonly API,并且优化之前写的reactive API。

在这里插入图片描述

实现readonly

readonly函数相信你在学习vue3的初始api时一定看到过,很简单的功能就是让调用这个函数的响应式对象只读,也就是无法set操作。

1.我们先看下单元测试代码

import { readonly } from "../reactive";

describe("readonlu", () => {
  it("happy path", () => {
    // not set
    const original = { foo: 1, bar: { baz: 2 } };
    const wrapped = readonly(original)
    expect(wrapped).not.toBe(original)
    expect(wrapped.foo).toBe(1)
  });
});

2.实现readonly

在reactive.ts文件下导出这个函数

export const readonly = (raw) => {
    return new Proxy(raw,{
        get(target,key,receiver) {
            const res =  Reflect.get(target,key,receiver)
            return res

        },
        set(target,key,value,receiver) {
            return true
        },

    })
}

readonly真的很简单,我们的目的是要优化这些函数,readonly和reactive函数的代码高度相似,vue3就重构抽离了这些代码,来增加代码可读性。

3.优化reactive和readonly

reactive和readonly两个函数中代码和逻辑实在太相似了,如果重构抽离相似代码的话,就一点也不优雅了!

优化前:
在这里插入图片描述
所以我们要在原基础上对代码重构并且优化,让它们变得更加优雅~

优化后:
在这里插入图片描述

优雅~实在是太优雅了!!

3.1 重构get和set操作

vue3把get放入到了一个高阶函数creatGetter中,高阶函数返回get,set也同理,我们现在来封装一下:

function createGetter(isReadonly = true) {
  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, receiver) {
    const res = Reflect.set(target, key, value, receiver)
    trigger(target, key)
    return res
  }
}

export const reactive = (raw) => {
    return new Proxy(raw,  {
  		get: createGetter(),
  		set: createSetter()
	})
}

export const readonly = (raw) => {
    return new Proxy(raw, {
        get: createGetter(false),
  		set(target, key, value, receiver) {
    		return true
  		},
    })
}

其中get我们可以通过给定一个传参去判断是否是readonly,而readonly的set有些特殊我们先不进行修改~

但是这个代码还是不够美观,继续优化

3.2 baseHandlers

在src目录下新建一个baseHandlers.ts文件
并且把之前封装好的creatGetter,creatSetter提取到这个文件夹中

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

function creatGetter(isReadonly = true) {
  return function get(target, key) {
    const res = Reflect.get(target, key)
    if (isReadonly) {
      track(target, key)
    }
    return res
  }
}

function creatSetter() {
  return function set(target, key, value, receiver) {
    const res = Reflect.set(target, key, value, receiver)
    trigger(target, key)
    return res
  }
}


//返回这个对象
export const mutableHandlers = {
  get: creatGetter(),
  set: creatSetter()
}

export const readonlyHandlers = {
  get: creatGetter(false),
  set(target, key, value, receiver) {
    return true
  },
}

然后我们再去reactive中引入mutableHandlers,readonlyHandlers即可

3.3 createActiveObject函数

为了语义化vue3又把new Proxy的操作也抽离成了createActiveObject函数

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



export const reactive = (raw) => {
    return createActiveObject(raw, mutableHandlers)
}

export const readonly = (raw) => {
    return createActiveObject(raw, readonlyHandlers)
}

function createActiveObject(raw,readonlyHandlers) {
    return new Proxy(raw, readonlyHandlers)
}
3.4 优化get和set捕获器

我们回到baseHandlers.ts文件里来,最后一步优化

提问:我们有必要每次生成proxy对象时都去creatGetter和creatSetter 生成get和set吗?

显示是没有必要的,所以我们可以利用缓存技术,继续优化代码

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

export const mutableHandlers = {
  get,
  set
}

export const readonlyHandlers = {
  get: readonlyGet,
  set(target, key, value, receiver) {
    return true
  },
}
3.5 警告用户不能set操作

这还没有结束,我们的readonly还差一个触发set操作时,警告用户无法set的功能

export const readonlyHandlers = {
  get: readonlyGet,
  set(target, key, value, receiver) {
    console.warn(`key:${key} set失败,因为target是readonly的`,target)
    return true
  },
}

新增一个单元测试,测试:用户进行set操作时警告用户无法set操作!

it("warn then call set", () => {
    console.warn = jest.fn()
    const user = readonly({
      age: 10
    })
    user.age = 11
    expect(console.warn).toBeCalled()
  })

成功通过所有测试了!
在这里插入图片描述

下节预告《vue3源码实现 isReactive 和 isReadonly》

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值