1. 1. Ref
  2. 2. ShadowRef
Vue3源码学习——响应式原理(02)

上文中大概解析了reactive的实现原理,本文主要解析ref的实现原理 。

Ref

refreactive的主要区别在于,ref可以将 JS 中的基本数据类型(如字符串、布尔值等)与引用类型转换为响应式对象,而reactive则只能转换引用类型数据。

我们可知ref执行后返回的是一个RefImpl类,我们在获取ref后的值需要通过.value获取,因此我们可以大概得出RefImpl的大致结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class RefImpl {
constructor(rawValue) {
this.__v_isRef = true
this._rawValue = rawValue
// 如果是引用类型,相当于再转成reactive
this._value = isObject(rawValue) ? reactive(rawValue) : rawValue
}
get value() {
// 依赖收集
// 当前this是一个对象 value作为key 也就是最终返回的 ref.value
track(this, 'value')
return this._value
}
set value(newValue) {
// 当新的值不等于老的值的话,才更新值,需要触发依赖
if (!hasChanged(newValue, this._rawValue)) {
this._value = newValue
this._rawValue = newValue
// 触发依赖
trigger(this, 'value')
}
}
}

创建ref函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export const ref = (value) => {
return createRef(value)
}

function createRef(rawValue) {
// 如果已经被ref代理过直接返回
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue)
}

/** 根据 __v_isRef 属性判断是否被ref代理过 */
function isRef(value) {
return !!value.__v_isRef
}

ShadowRef

shadowRef的官方解释为ref的浅层作用形式,可以理解为当shadowRef传入的是引用类型数据时,只有引用地址变化时才会触发依赖更新,而改变引用类型的属性时不会触发依赖更新,常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。

我们可以给代码RefImpl增加一个 shadow 入参,来区分两者的使用:

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
function convert(val) {
return isObject(val) ? reactive(val) : val
}

class RefImpl {
constructor(rawValue, shadow = false) {
this.__v_isRef = true
this._shallow = shadow
this._rawValue = rawValue
// 如果是shadowRef相当于直接返回原数据
// 如果是引用类型,相当于再转成reactive
this._value = shadow ? rawValue : convert(rawValue)
}
get value() {
// 依赖收集
// 当前this是一个对象 value作为key 也就是最终返回的 ref.value
track(this, 'value')
return this._value
}
set value(newValue) {
// 当新的值不等于老的值的话,才更新值,需要触发依赖
if (!hasChanged(newValue, this._rawValue)) {
this._value = newValue
this._rawValue = newValue
// 触发依赖
trigger(this, 'value')
}
}
}

export const ref = (value) => {
return createRef(value)
}

export const shadowRef = (value) => {
return createRef(rawValue, true)
}

function createRef(rawValue, shadow) {
// 如果已经被ref代理过直接返回
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shadow)
}