实现 ref 函数

isRef 判断一个数据是否是用 ref 声明的响应式数据

单测

1
2
3
4
5
6
7
8
9
10
11
//reactivity/__test__/ref.spec.ts
...
it('isRef', () => {
const a = ref(1)
const user = reactive({
age: 1
})
expect(isRef(a)).toBe(true)
expect(isRef(1)).toBe(false)
expect(isRef(user)).toBe(false)
})

实现单测

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
//reactivity/ref.ts
...
class RefImpl {
private _value: any
public dep
private _rawValue: any
public __v_isRef = true
constructor(value) {
this._rawValue = value
this._value = convert(value)

this.dep = new Set()
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = convert(newVal)
triggerEffects(this.dep)
}
}
}
export const isRef = ref => !!ref.__v_isRef;

实现 unRef 函数

unRef 省略了 ref.value 繁琐操作,直接获取值

单测

1
2
3
4
5
6
7
//reactivity/__test__/ref.ts
...
it('unRef', () => {
const a = ref(1)
expect(unRef(a)).toBe(1)
expect(unRef(1)).toBe(1)
})

实现单测

1
2
3
4
//reactivity/ref.ts
...
export const unRef = ref => (isRef(ref) ? ref.value : ref)

实现 proxyRefs 函数

proxyRefs主要用来在 vue3 中 template 的 setup 去转换一下 ref 类型,省去了 .value 繁琐操作。
本质就是一个 proxy,代理了一个取值,修改的功能,取值功能就是一个 unRef,修改的时候,需要判断被修改属性是否是 ref 类型,如果是 ref 类型,且新值为非 ref 类型就是 替换掉被修改属性的 .value;如果被修改属性和新值都为 ref 类型,则直接替换掉被修改属性即可。

单测

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
//reactivity/__test__/ref.spec.ts
...
it('proxyRefs', () => {
const user = {
age: ref(10),
name: 'qiuSu'
}
/**
* get操作读取key判断是否是ref类型
* 是 返回.value
* 否 直接返回
*/
const proxyUser = proxyRefs(user)
expect(user.age.value).toBe(10)
expect(proxyUser.age).toBe(10)
expect(proxyUser.name).toBe('qiuSu')
/**
* set操作修改val判断是否是ref类型
* 是 修改.value
* 否 直接修改
*/
proxyUser.age = 20
expect(proxyUser.age).toBe(20)
expect(user.age.value).toBe(20)
})

实现单测

1
2
3
4
5
6
7
8
9
10
11
12
13
//reactivity/ref.ts
...
export const proxyRefs = objectWithRefs => {
return new Proxy(objectWithRefs, {
get(target, key) {
return unRef(Reflect.get(target, key))
},
set(target, key, val) {
if (isRef(target[key]) && !isRef(val)) return (target[key].value = val)
else return Reflect.set(target, key, val)
}
})
}