重构reactive
本来我们应该直接实现readonly
模块的,但是其实它本质上和reactive
很像,只不过它是只读的,不能set
,set时,不会设置成功只会警告,所以相似代码很多,我们开发之前先重构一下reactive
分析一下这个响应式场景下proxy
的两个形参,影响最主要的是第二个参数handler
, 也就是一个包含get set函数的对象
1
2
3
4
|
{
get(target, key) {},
set(target, key, value) {},
}
|
所以我们先把这部分拆分出去,在reactivity
目录下创建baseHandler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// reactive.ts 改造如下
import {
mutableHandler,
mutableHandlerReadonly,
} from './baseHandler'
function reactive(raw) {
return createReactiveObject(raw, mutableHandler)
}
function readonly(raw) {
return creativeReactiveObject(raw, mutableHandlerReadonly)
}
// 根据不同的handler统一创建代理对象,相当于中间层,解耦响应式和handler处理器
function creativeReactiveObject(target, baseHandler) {
return new Proxy(target, baseHandler)
}
|
上面的mutableHandler,mutableHandlerReadonly
就是要重点重构的两个函数,导出的这两个对象就是上面分析的这类对象:
1
2
3
4
|
{
get(target, key) {},
set(target, key, value) {},
}
|
详细的代码逻辑:
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
|
// baseHandler.ts
import { trigger, track } from './effect'
// 这样写的目的是能缓存这些函数值,只在文件初始化时加载一次即可
const get = createGetter()
const getReadonly = createGetter(true)
const set = createSetter()
function createGetter(isReadonly: Boolean = false) {
return function get(target, key) {
const value = Reflect.get(target, key)
if(!isReadonly) {
// 不是readonly才需要收集依赖
track(target, key)
}
return value
}
}
function createSetter() {
return function set(target, key, value) {
const value = Reflect.set(target, key, value)
trigger(target, key)
return value
}
}
const mutableHandler = {
get,
set,
}
const mutableHandlerReadonly = {
get: getReadonly,
set(target, key, value) {
console.warn(`key :"${String(key)}" set 失败,因为 target 是 readonly 类型`, `${JSON.stringfy(target)}`)
return true
}
}
export {
mutableHandler,
mutableHandlerReadonly,
}
|
实现readonly
其实经过上面的重构,我们已经实现完了readonly()
,它是响应式核心之一。而且后期我们实现新的代理会变的很容易,直接增加对应的baseHandler
即可,先看一下测试用例的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import { readonly } from "../reactive";
describe("readonly", () => {
it('happy path', () => {
const origin = {
foo: 1
}
const observed = readonly(origin)
expect(observed.foo).toBe(1)
expect(observed).not.toBe(origin)
});
it('console warn when you set value', () => {
const origin = {
foo: 1
}
console.warn = jest.fn()
const observed = readonly(origin)
observed.foo = 2
expect(console.warn).toHaveBeenCalled()
});
})
|
其实readonly
几乎和reactive
一样,都是响应式的,只是不能修改属性值。
文章作者
吴少林
上次更新
2022-10-10
(c26813b)
许可协议
MIT