手写mini版本的Vue3--实现 readonly 功能

208 阅读2分钟

介绍

开头继续感谢崔大的mini-vue 项目。

项目地址:github.com/cuixiaorui/…

强烈建议大家把项目 down 下来,运行一下,看VUE3 的整体运行逻辑。

今天我们要实现的是 readonly 的功能,以及优化一下我们目前的代码。

实现 readonly

首先,我们要知道 readonly 是做什么的,简单来说,就是只能读取值,不能设置值。

也就是说,跟我们的 reactive 相比,是get的时候只需要返回值即可,不需要追踪我们的依赖。

不能设置值,意味着我们可以没有 set 方法,不过我们还是可以给 set 方法返回一些错误,告诉用户。

以下就是我们要实现的一个readonly 的功能。 可以看到,跟reactive一样的构造,就是不需要 set,get 也只是直接返回。

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

优化代码

优化get set 方法

可以看到我们的readonly 的 get 和 reactive 的 get 差不多,这个情况我们就可以封装一下。

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

然后在readonly和 reactive 都可以调用 createGetter 这个函数,只不过 readonly 的时候传入 true。

然后我们的 set 也是可以封装成一个createSetter函数,在 reactive set 的时候调用即可。

优化 proxy 方法

我们还可以继续抽离,readonly和 reactive 都是使用的 new proxy 这个结构。

我们可以创建一个 basehandle 的文件,把 createSetter,createGetter 也放到新文件里面。

然后我们使用mutableHandlers和readonlyHandlers 给导出到 reactive 里面就好。

可以看到就是下面这样子。

export function reactive(raw) {
  return new Proxy(raw, mutableHandlers)
}
export function readonly(raw) {
  return new Proxy(raw, readonlyHandlers)
}

当然了,我们可以继续抽离把这个reactive,readonly里面的 new Proxy 再抽离成一个createReactiveObject。然后传入即可。

再优化一下 set,get

现在我们的结构是 get:createGetter(), set:createSetter()

这个时候,我们每次调用 set 和 get 都会重新去创建一次,但是我们实际上并不需要。

也就是说我们可以使用一个 set 变量来保存我们的函数 createSetter() get 同理。

还有一个就是我们可以把 readonly 调用 set 的时候,警告⚠️用户

set(target, key) {
    console.warn(
      `key :"${String(key)}" set 失败,因为 target 是 readonly 类型`,
      target
    )

    return true
  },

结尾

项目已经放到我的 GitHub 上面了,欢迎大家去start。

本次 commit地址:github.com/moyuhaokan/…

我的项目地址:github.com/moyuhaokan/…

再次推荐崔大的项目:github.com/cuixiaorui/…