Readonly
readonly
其实跟reactive
是一个原理,都是一个proxy
对象,只是readonly
不能被set
,这样就不会涉及到触发依赖,当然也就不需要去收集依赖。
编写单测
下面,还是通过一个单测来了解一下它的功能点。
// src/reactivity/tests/readonly.spec.ts
import {readonly} from '../reactive'
describe("readonly", () => {
it("happy path", () => {
const original = {
foo: 1,
bar: {
baz: 2
}
}
const wrapped = readonly(original)
expect(wrapped).not.toBe(original)
expect(wrapped.foo).toBe(1)
})
})
复制代码
其实readonly
就仅仅是一个静态响应式对象。
编码
readonly
其实就是一个简版的reactive
,所以逻辑还是写在reactive.ts
中。
1、readonly函数
// src/reactivity/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
}
})
}
复制代码
导出readonly
函数,将接收的对象包装成proxy
对象并且返回。
代码优化
实现功能就是这么简单,接下来对我们的代码进行优化,将reactive.ts
中类似的代码抽离出来,形成通用的,方便后续扩展其他的。
1、将公共代码抽离到baseHandlers.ts
将Proxy
的handler
全部抽离到一个文件,统一管理,后续再添加也比较方便。
// src/reactivity/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 get (target, key, value) {
const res = Reflect.set(target, key, value)
trigger(target, key)
return res
}
}
export const mutableHandlers = {
get,
set
}
export const readonlyHandlers = {
get: readonlyGet,
set (target, key, value) {
return true
}
}
复制代码
通过创建高阶函数createGetter
和createSetter
统一处理handler
的调配,并通过创建全局变量get
,set
,readonlyGet
存储,避免多次创建。
2、优化reactive
基于baseHandler.ts
导出的mutableHandlers
和readonlyHandlers
对reactive.ts
进行简化
// src/reactivity/reactive.ts
import { mutableHandlers, readonlyHandlers } from './baseHandlers'
export function reactive (raw) {
return new Proxy(raw, mutableHandlers)
}
export function readonly (raw) {
return new Proxy(raw, readonlyHandlers)
}
复制代码
这样,我们的代码就重构完了。
完善功能
目前已经完成readonly
的功能,并且在给readonly
赋值的时候也没有进行任何处理,接下来,我们要实现给readonly
赋值的时候给用户一个提示。
编写测试用例
// src/reactivity/tests/readonly.spec.ts
describe("readonly", () => {
...
it("warn when call set", () => {
console.warn = jest.fn()
const user = readonly({
age: 10
})
user.age = 11
expect(console.warn).toBeCalled()
})
})
复制代码
当用户给readonly
的属性赋值的时候,会执行console.warn
,给用户一个提示信息。
完善代码
在readonlyHandler
中的set
添加一个提示消息。
// src/reactivity/baseHandlers.ts
...
export const readonlyHandlers = {
...,
set (target, key, value) {
console.warn(`The ${key} set to ${value} faild, beacuse the target: ${JSON.stringify(target)} is readonly!`)
...
}
}
复制代码
总结
这一节,先通过Proxy
创建了一个只处理了get
请求的响应式对象,也就是我们的readonly
,后面通过代码重构,将reactive
和readonly
相同的代码进行抽离封装,让代码更具可读性和可扩展性,最后给readonly
处理一下set
操作的提示消息,上面就是readonly
的所有功能及代码。
下一节,我们一起来实现isReactive
和isReadonly
。