ref

接收一个内部值并返回响应式且可变的ref对象,ref对象仅有一个.value属性,指向该内部值。

浏览器控制台设置里,勾选“启动自定义格式化程序”并刷新,控制台就能看格式化后整齐的ref,reactive也是如此。

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
<template>
<div>{{person}}</div>
<button @click="change">修改</button>
<div ref="dom">我是个DOM</div>
</template>

<script setup lang="ts">
import { ref } from "vue"
import type { Ref } from "vue"

type P = {
name: string
}

// ref 作深层响应式
// 也可以不写类型定义或泛型,ref会做类型推断
// const person = ref<P>({ name: "yajue" })
// const person: Ref<P> = ref({ name: "yajue" })
const person = ref({ name: "yajue" })

// 不用ref包裹就没有响应式
const person2 = { name: "pinky" }

// ref还可以取得DOM元素,变量名需要和元素的ref属性值一致
const dom = ref<HTMLDivElement>()

// 在setup语法糖里只能读到undefined,因为在这个阶段,DOM还没有被渲染
console.log(dom.value?.innerText)

const change = ()=> {
// person.name = "野兽先辈"
// ref函数返回一个类,其中有一个属性value,修改或取值时必须带上value
person.value.name = "野兽先辈"
console.log(person)
console.log(dom.value?.innerText)
}

</script>

isRef

判断是否为ref对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>{{person}}</div>
<button @click="change">修改</button>
</template>

<script setup lang="ts">
import { ref, isRef } from "vue"

const person = ref({ name: "yajue" })

const person2 = { name: "pinky" }

const change = ()=> {
// 判断对象是不是ref对象
// 实际生产中用得不多,但源码里用得很多
console.log(isRef(person)) // -> true
console.log(isRef(person2)) // -> false
}

</script>

shallowRef

创建一个跟踪.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
26
27
28
29
30
31
32
33
34
35
36
<template>
<div>ref: {{person}}</div>
<hr>
<div>shallowRef: {{person2}}</div>
<hr>
<button @click="change">修改</button>
</template>

<script setup lang="ts">
import { ref, shallowRef } from "vue"

type P = {
name: string
}

const person = ref({ name: "yajue" })

// shallowRef 作浅层响应式
const person2 = shallowRef({ name: "MUR" })

const change = ()=> {
// 点击后数据改变,但视图不会变化,因为它是浅层响应式
// person2.value.name = "三浦"

// 直接从value赋值才会变成响应式
// person2.value = {
// name: "三浦"
// }

// ref和shallowRef不能同时写,因为会影响shallowRef,造成视图的更新
person.value.name = "我是ref"
person2.value.name = "我是shallowRef"
console.log(person2)
}

</script>

triggerRef

强制页面视图更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>ref: {{person}}</div>
<hr>
<div>shallowRef: {{person2}}</div>
<button @click="change">修改</button>
<hr>
</template>

<script setup lang="ts">
import { ref, shallowRef, triggerRef } from "vue"

const person = ref({ name: "yajue" })

const person2 = shallowRef({ name: "MUR" })

const change = ()=> {
person2.value.name = "我被影响了"
// triggerRef强制更新收集的依赖,直接调用它也能更新shallowRef
// ref底层更新视图的逻辑中会调用triggerRef函数,所以ref和shallowRef不能同时写在一起
triggerRef(person2)
console.log(person2)
}

</script>

customRef

自定义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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template>
<div>{{obj}}</div>
<button @click="change">修改</button>
</template>

<script setup lang="ts">
import { ref, shallowRef, triggerRef, customRef } from "vue"

// 自定义ref,要求返回一个customRef的返回值
function myRef<T>(value: T) {
// customRef接收一个回调函数,回调函数接收两个参数
return customRef((track, trigger)=> {

// 可以使用自定义ref实现额外的逻辑
let timer

// 回调函数要求返回一个对象,对象需要实现get和set方法
return {
get() {
// track用来收集依赖
track()
return value
},
set(newVal) {
//比如用户输入值时,调用接口并做个防抖
clearTimeout(timer)
setTimeout(()=> {
console.log("我调用了一个接口")
value = newVal
timer = null
// trigger用来触发依赖更新
trigger()
}, 500)

}
}
})
}

const obj = myRef<string>("cheriko")

const change = ()=> {
obj.value = "meruko"
console.log(obj)
}

</script>

源码

在Vue源码(/package/reactivity/src/ref.ts)中可以看到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
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
/**
* Takes an inner value and returns a reactive and mutable ref object, which
* has a single property `.value` that points to the inner value.
*
* @param value - The object to wrap in the ref.
* @see {@link https://vuejs.org/api/reactivity-core.html#ref}
*/
// 进行函数重载,支持多种传入的类型
export function ref<T extends Ref>(value: T): T
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
// value就是程序员传进ref的值
export function ref(value?: unknown) {
return createRef(value, false)
}

// ----------------------------------------------------------------

export function shallowRef<T extends object>(
value: T
): T extends Ref ? T : ShallowRef<T>
export function shallowRef<T>(value: T): ShallowRef<T>
export function shallowRef<T = any>(): ShallowRef<T | undefined>
// shallowRef,和ref的不同就是它把__v_isShallow设为true,所以它的响应只到.value
export function shallowRef(value?: unknown) {
return createRef(value, true)
}

// ----------------------------------------------------------------

function createRef(rawValue: unknown, shallow: boolean) {
// 判断传进的值,如果已经是ref对象就不用再变了
if (isRef(rawValue)) {
return rawValue
}
// 创建ref对象
return new RefImpl(rawValue, shallow)
}

// ----------------------------------------------------------------

class RefImpl<T> {
// 这个_value就是真正要读取的东西
private _value: T
private _rawValue: T

public dep?: Dep = undefined
public readonly __v_isRef = true

// 构造函数
constructor(value: T, public readonly __v_isShallow: boolean) {
this._rawValue = __v_isShallow ? value : toRaw(value)
// 判断isShallow,如果为true就赋值value,为false就赋值toReactive(value)
this._value = __v_isShallow ? value : toReactive(value)
}

get value() {
// 进行依赖收集
trackRefValue(this)
// 读取的是_value
return this._value
}

set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
// 修改的也是_value
this._value = useDirectValue ? newVal : toReactive(newVal)
// 进行依赖更新
triggerRefValue(this, newVal)
}
}
}

// ----------------------------------------------------------------

// triggerRef,可以强制更新shallowRef的值
export function triggerRef(ref: Ref) {
triggerRefValue(ref, __DEV__ ? ref.value : void 0)
}
// triggerRefValue会接着调用triggerEffects,它会进行依赖的更新

这里属于reactive的源码,位于/package/reactivity/src/reactive.ts

1
2
3
4
5
6
7
8
9
10
/**
* Returns a reactive proxy of the given value (if possible).
*
* If the given value is not an object, the original value itself is returned.
*
* @param value - The value for which a reactive proxy shall be created.
*/
// 判断传入的value是否为引用类型(数组或对象),是则调用reactive,否则返回value本身
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value