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
}
结语
这里优化使用了高阶函数,初始化仅创建一次得思路,抽离公共区域,也比较有参考意义,感谢