实现shallowReadonly功能
shallowReadonly: 让一个响应式数据变为只读的(浅只读)
接下来附上测试用例:
import { isReadonly,shallowReadonly } from "../reactive"
describe('shallowReadonly',()=>{
test('should not make non-reactive properties reactive',()=>{
// shallowReadonly: 让一个响应式数据变为只读的(浅只读)
const props = shallowReadonly({n:{foo:1}})
expect(isReadonly(props)).toBe(true)
expect(isReadonly(props.n)).toBe(false)
})
it('warn then call set',()=>{
console.warn = jest.fn()
const user = shallowReadonly({
age:10
})
user.age = 11
expect(console.warn).toBeCalled()
})
})
其实有了前面对响应式对象那一套的抽离后,现在要加入shallowReadonly功能也就很简单了(主要涉及的文件reactive.ts,baseHandlers.ts):
1 在reactive.ts中导出shallowReadonly这个方法
2 shallowReadonly中传入createReactiveObject中的shallowReadonlyHandler从我们之前baseHandlers文件中导入
3 baseHandlers中我们需要先根据条件生成对应shallowReadonlyGet
4 然后就是导出shallowReadonlyHandlers,通过封装的extend方法对readonlyHandlers进行扩展修改
下面我将按顺序给出步骤图片,并且注上解释
涉及代码文件的整体代码:
reactive.ts
import { mutableHandlers, readonlyHandlers,shallowReadonlyHandlers } from "./baseHandlers"
export const enum ReactiveFlags {
IS_REACTIVE = "__v_isReactive",
IS_READONLY = "__v_isReadonly"
}
export function reactive(raw){
return createReactiveObject(raw,mutableHandlers)
}
export function readonly(raw){
return createReactiveObject(raw,readonlyHandlers)
}
export function shallowReadonly(raw){
return createReactiveObject(raw,shallowReadonlyHandlers)
}
export function isReactive(value){
return !!value[ReactiveFlags.IS_REACTIVE]
}
export function isReadonly(value){
return !!value[ReactiveFlags.IS_READONLY]
}
function createReactiveObject(target,baseHandlers){
return new Proxy(target,baseHandlers)
}
baseHandler.ts
import { extend, isObject } from "../shared"
import { track, trigger } from "./effect"
import { reactive, ReactiveFlags, readonly } from "./reactive"
const get = createGetter()
const set = createSetter()
const readonlyGet = createGetter(true)
const shallowReadonlyGet = createGetter(true,true)
function createGetter(isReadonly = false,shallow = false){
return function get(target,key){
if(key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
}else if(key === ReactiveFlags.IS_READONLY) {
return isReadonly
}
const res = Reflect.get(target,key)
// 如果shallow为true
if(shallow){
return res
}
// 看看 res 是不是 object
if(isObject(res)){
return isReadonly ? readonly(res) : reactive(res)
}
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
}
}
export const shallowReadonlyHandlers = extend({},readonlyHandlers,{
get:shallowReadonlyGet
})