实现 readonly 只读功能

readonly顾名思义就是一个只读功能,不能修改

单测

1
2
3
4
5
6
7
8
9
10
//reactivity/__test__/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)
})
})

实现单测

1
2
3
4
5
6
7
8
9
10
11
12
13
//reactivity//reactive.ts
...
export const readonly = raw => {
return new Proxy(raw, {
get: (target, key) => {
const res = Reflect.get(target, key)
return res
},
set: (target, key, value) => {
return true
}
})
}

重构代码抽离到公共文件

1
2
3
//shared/index.ts
...
export const isObject = val => val !== null && typeof val === 'object'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//reactivity/reactive.ts
...
import { isObject } from '../shared/index'
import { mutableHandler, readonlyHandler } from './baseHandlers'
/**响应式对象实现 */
export const createReactiveObject = (raw, baseHandlers) => {
if (!isObject(raw)) {
console.warn(`target ${raw} 必须是一个对象`)
return raw
}
return new Proxy(raw, baseHandlers)
}

export const reactive = raw => createReactiveObject(raw, mutableHandler)
export const readonly = raw => createReactiveObject(raw, readonlyHandler)
```k

```ts
//reactivity/baseHandler.ts
...
import { track, trigger } from './effect'
import { isObject, extend } from '../shared/index'
import { reactive, readonly } from './reactive'
//创建get
export const createGetter = (isReadonly = false) => {
return function get(target, key) {
const res = Reflect.get(target, key)
/**嵌套转换 */
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
if (!isReadonly) {
track(target, key)
}
return res
}
}
//创建set
export const createSetter = () => {
return function set(target, key, value) {
const res = Reflect.set(target, key, value)
trigger(target, key)
return res
}
}
//缓存get跟set,始终使用同一个
const get = createGetter()
const set = createSetter()
const readonlyGet = createGetter(true)

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