读该文章之前,你需要先了解Proxy相关的知识。

最近读vue3源码,isRef使用了一个其实非常非常简单的办法。
比如下面这段代码:

1
2
3
4
5
6
7
class Ref {
public __v_isRef = true
// 接收参数忽略
}
function ref(val) {
return new Ref()
}

若你想判断obj是否是ref,其实很简单:

1
2
3
4
5
6
function isRef(value) {
return !!value.__v_isRef
}
const obj = ref(18)
isRef(obj) // true
isRef({}) // false

但isReactive和isReadonly不太一样。他们俩会先设定好一个map:

1
2
3
4
5
6
7
8
9
10
export const ReactiveFlags = {
IS_REACTIVE: '__v_isReactive',
IS_READONLY: '__v_isReadonly',
}
function isReactive(raw) {
return !!raw[ReactiveFlags.IS_REACTIVE]
}
export function isReadonly(raw) {
return !!raw[ReactiveFlags.IS_READONLY]
}

然后,如果你调用isReactive,就会去触发get:

1
2
3
4
const obj = reactive({
age: 18
})
isReactive(obj) // isReactive函数内部执行return !!obj.__v_isReactive,此时会触发get

接着,在Proxy get里面,单独对该key做一个判断,如果访问的是该key,就return true:

1
2
3
4
// 这里暂不考虑是readonly的情况,否则if中需要再加上相关判断条件
if (key === ReactiveFlags.IS_REACTIVE) {
return true
}

这里get返回true,因此isReactive返回true。(并且Proxy get后续代码不需要执行。也就是说,如果是访问了__v_isReactive这个字段,直接return true)

1
2
3
4
const obj = reactive({
age: 18
})
isReactive(obj) // true

说到这里,再来一个computed的简易原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ComputedRefImpl {
private getter: Function
constructor(getter) {
this.getter = getter
}
get value() {
return this.getter()
}
}

export function computed(getter: Function) {
const computedRef = new ComputedRefImpl(getter)
return computedRef
}

const user = reactive({
age: 1,
})
const computedAge = computed(() => {
return user.age
})

computed里面的回调函数会返回一个值,然后在ComputedRefImpl类中get value返回该函数的值,最终,当你使用computedAge.value的时候就会去触发get,获取该callback的返回值。