从上一篇文字中,已经知道主要的reactive处理响应式通过apiproxy来实现,但是具体后续的逻辑是在proxy的各个钩子函数上实现,这篇主要看baseHandlers.ts文件,它主要是处理数据类型是ObjectArray的数据。一切都在代码中,开工。

在阅读reactive.ts时,已经知道不同的API调用createReactiveObject函数时传入不同的Handlers,这里先看一下不同的Handlers的具体代码,在查看公共代码

mutableHandlers

1
2
3
4
5
6
7
8
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}

mutableHandlers最普通,它直接调用公共实现的各个方法,下面具体看下每个方法的实现

deleteProperty

1
2
3
4
5
6
7
8
9
10
11
12

function deleteProperty(target: object, key: string | symbol): boolean {
const hadKey = hasOwn(target, key)
const oldValue = (target as any)[key]
const result = Reflect.deleteProperty(target, key)
if (result && hadKey) {
// 触发依赖
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}

has

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @info: 检查一个对象是否拥有某个属性
* @param {target} 目标对象
* @param {key} 键值
* @return {Boolean}
*/
function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key)
if (!isSymbol(key) || !builtInSymbols.has(key)) {
track(target, TrackOpTypes.HAS, key)
}
return result
}

ownKeys

1
2
3
4
5
6
7

// 返回一个由目标对象自身的属性键组成的数组
function ownKeys(target: object): (string | symbol)[] {
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
return Reflect.ownKeys(target)
}

get|createGetter

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

/* 内部涉及到的函数 */

const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
val: object,
key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)

const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations()

/* 对数组的一些API进行跟进 */
function createArrayInstrumentations() {
const instrumentations: Record<string, Function> = {}
// instrument identity-sensitive Array methods to account for possible reactive
// 仪器身份敏感数组方法来考虑可能的反应
// values
;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
// 拿到原始对象处理
const arr = toRaw(this) as any
for (let i = 0, l = this.length; i < l; i++) {
// 收集
track(arr, TrackOpTypes.GET, i + '')
}
// we run the method using the original args first (which may be reactive)
// 我们首先使用原始参数运行该方法(这可能是 reactive)
const res = arr[key](...args)
if (res === -1 || res === false) {
// if that didn't work, run it again using raw values.
// 如果这不起作用,使用原始值再次运行它。
return arr[key](...args.map(toRaw))
} else {
return res
}
}
})
// instrument length-altering mutation methods to avoid length being tracked
// which leads to infinite loops in some cases (#2137)
// 仪器改变长度的突变方法以避免长度被跟踪
// 在某些情况下会导致无限循环 (#2137)
;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
pauseTracking() // 依赖处理相关API
const res = (toRaw(this) as any)[key].apply(this, args)
resetTracking()
return res
}
})
return instrumentations
}


/* 内部涉及到的函数 */

// 直接调用 createGetter,并且入参都是默认入参
const get = /*#__PURE__*/ createGetter()

/**
* @description: 用于拦截对象的读取属性操作
* @param {isReadonly} 是否只读
* @param {shallow} 是否浅观察
*/
function createGetter(isReadonly = false, shallow = false) {
/**
* @description:
* @param {target} 目标对象
* @param {key} 需要获取的值的键值
* @param {receiver} 当前的代理实例
*/
return function get(target: Target, key: string | symbol, receiver: object) {
// 开始先处理内部的各个标记,并进行返回对于的boolean
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
return shallow
} else if (
// 这是处理 toRAW 返回被代理的对象
// 但是没明白为什么不直接返回对象,而是要判断下当前代理是不是被记录的代理
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}

const targetIsArray = isArray(target)
if (!isReadonly) {
// 如果目标对象是数组并且 key 属于三个方法之一 ['includes', 'indexOf', 'lastIndexOf'],即触发了这三个操作之一
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
// 拿到需要返回的值
const res = Reflect.get(target, key, receiver)

// 如果 key 是 symbol 内置方法,或者访问的是原型对象,直接返回结果,不收集依赖
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}

// 不为只读则调用 track Get
// track Get 应该就是观察者的依赖收集的动作具体的后续继续看
// 并且从代码来看,收集了当前的数据对象,收集的类型,当前触发的key
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}

// 如果是浅观察返回结果
if (shallow) {
return res
}

// 如果是ref,直接调用ref的value返回值
if (isRef(res)) {
// 如果对象是数组并且key是数字直接返回数组的下标对应的值
// ref unwrapping - skip unwrap for Array + integer key.
// ref展开 - 跳过数组的展开 + 整数 键
return targetIsArray && isIntegerKey(key) ? res : res.value
}

// 从这里可以看出,对于多纬JSON或数组并不是一开始就转换为代理。只有在读取到的时候才进行转换
if (isObject(res)) {
// Convert returned value into a proxy as well. we do the isObject check
// here to avoid invalid value warning. Also need to lazy access readonly
// and reactive here to avoid circular dependency.
// 也将返回值转换为代理。 我们做 isObject 检查
// 这里是为了避免无效值警告。 还需要延迟访问只读
// 并在这里反应以避免循环依赖。
// 这里就是如果是对象,就有递归处理将对象处理成代理
// 因为`proxy`API虽然比`Object.defineProperty`强大,但是它也只代理当前一层的对象,对于嵌套对象是不会自动深层代理的
// 需要代码递归处理
return isReadonly ? readonly(res) : reactive(res)
}

// 返回取得的值
return res
}
}

set | createSetter

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
58
59
60
61
62
63
64
65
66
67
68
/* 涉及到的函数 */

export const hasChanged = (value: any, oldValue: any): boolean =>
!Object.is(value, oldValue)

/* 涉及到的函数 */

/**
* @info 返回set钩子执行的函数
* @param shallow 是否浅引用
* */
function createSetter(shallow = false) {
/**
* @info set 钩子的回调函数
* @param target 当前被代理的对象
* @param key 需要修改值的key name
* @param value 最新的值
* @param receiver 当前代理的实例
* */
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
// 获取修改前的值
let oldValue = (target as any)[key]
// 只读或者旧的值或新的值是ref时,停止修改
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false
}
if (!shallow) {
// 不管有没有被reactive都解析出原始数据
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
value = toRaw(value)
}
// ref直接设置值
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
} else {
// in shallow mode, objects are set as-is regardless of reactive or not
// 在浅层模式下,无论是否reactive,对象都按原样设置
}

// 检查对象是否有这个属性或数组下标赋值是否在当前数组中
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
// 修改原始对象的值
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
// 如果 target 是 original 原型链中的某个东西,则不要触发 依赖更新
if (target === toRaw(receiver)) {
if (!hadKey) {
// 如是不存在则trigger ADD
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 存在则trigger SET
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}

shallowReactiveHandlers

1
2
3
4
5
6
7
8
export const shallowReactiveHandlers = /*#__PURE__*/ extend(
{},
mutableHandlers,
{
get: shallowGet,
set: shallowSet
}
)

从代码来看与reactive相比,deletePropertyhasownKeys功能一致,但是重写了getset。具体都在代码中了

shallowGet

1
2
3
4
5
6
7
8
9
10
// 是不是很意外,表层处理的判断已经在get中处理了
const shallowGet = /*#__PURE__*/ createGetter(false, true)

// 单独拿出来在看一下
// ...other code
// 如果是浅观察返回结果
if (shallow) {
return res
}
// ...other code

shallowSet

1
2
3
4
5
6
7
8
9
10
// 是不是很意外,表层处理的判断已经在set中处理了
const shallowSet = /*#__PURE__*/ createSetter(true)

// 这里在单独拿出来看一下
// ...other code
else {
// in shallow mode, objects are set as-is regardless of reactive or not
// 在浅层模式下,无论是否reactive,对象都按原样设置
}
// ...other code

readonlyHandlers

mutableHandlers 相比get有一个新的钩子函数实现,setdeleteProperty操作都阻至并在开发
模式下发出警告

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
export const readonlyHandlers: ProxyHandler<object> = {
get: readonlyGet,
set(target, key) {
if (__DEV__) {
warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
},
deleteProperty(target, key) {
if (__DEV__) {
warn(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
}
}

// 逻辑也已经在 get 中判断处理
const readonlyGet = /*#__PURE__*/ createGetter(true)

// 方便后续查看列一些特殊操作

// 1. 从代码来看只读的数据不会调用依赖收集
// 2. 如果读取的数据是引用类型的话,从新调用readonly进行数据标记

shallowReadonlyHandlers

从代码来看与readonlyHandlers保持一致,但是重写了get方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export const shallowReadonlyHandlers = /*#__PURE__*/ extend(
{},
readonlyHandlers,
{
get: shallowReadonlyGet
}
)

const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)

// 方便查询
// 1.直接返回了数据没有别的操作了

if (shallow) {
return res
}

结尾

到这里可以得出结论对于proxy常见的钩子get,set,deleteProperty,has,ownKeys进行了监听

同时在钩子函数中出现了几个高频的外部函数track,trigger。这两个函数来自于effect.tseffectreactive 的核心。后面我们阅读它。