# Refs
本节使用单文件组件语法作为代码示例
# ref
接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性 .value
。
示例:
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
2
3
4
5
如果传入 ref 的是一个对象,将调用 reactive 方法进行深层响应转换。
类型定义:
interface Ref<T> {
value: T
}
function ref<T>(value: T): Ref<T>
2
3
4
5
有时我们可能需要为 ref 做一个较为复杂的类型标注。我们可以通过在调用 ref
时传递泛型参数来覆盖默认推导:
const foo = ref<string | number>('foo') // foo's type: Ref<string | number>
foo.value = 123 // ok!
2
3
如果泛型的类型未知,建议将 ref
转换为 Ref<T>
:
function useState<State extends string>(initial: State) {
const state = ref(initial) as Ref<State> // state.value -> State extends string
return state
}
2
3
4
# unref
如果参数是一个 ref
则返回它的 value,否则返回参数本身。它是 val = isRef(val) ? val.value : val
的语法糖。
function useFoo(x: number | Ref<number>) {
const unwrapped = unref(x) // unwrapped 一定是 number 类型
}
2
3
# toRef
ref
可以用来为一个 reactive 对象的属性创建一个 ref。这个 ref 可以被传递并且能够保持响应性。
const state = reactive({
foo: 1,
bar: 2,
})
const fooRef = toRef(state, 'foo')
fooRef.value++
console.log(state.foo) // 2
state.foo++
console.log(fooRef.value) // 3
2
3
4
5
6
7
8
9
10
11
12
当您要将一个 prop 中的属性作为 ref 传给组合逻辑函数时,toRef
就派上了用场:
export default {
setup(props) {
useSomeFeature(toRef(props, 'foo'))
},
}
2
3
4
5
# toRefs
把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
,和响应式对象 property 一一对应。
const state = reactive({
foo: 1,
bar: 2,
})
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型如下:
{
foo: Ref<number>,
bar: Ref<number>
}
*/
// ref 对象 与 原属性的引用是 "链接" 上的
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
当想要从一个组合逻辑函数中返回响应式对象时,用 toRefs
是很有效的,该 API 让消费组件可以 解构 / 扩展(使用 ... 操作符)返回的对象,并不会丢失响应性:
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2,
})
// 对 state 的逻辑操作
// 返回时将属性都转为 ref
return toRefs(state)
}
export default {
setup() {
// 可以解构,不会丢失响应性
const { foo, bar } = useFeatureX()
return {
foo,
bar,
}
},
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# isRef
检查一个值是否为一个 ref 对象。
# customRef
customRef
用于自定义一个 ref
,可以显式地控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别是用于追踪的 track
和用于触发响应的 trigger
,并返回一个带有 get
和 set
属性的对象。
使用
v-model
使用自定义 ref 实现debounce
的示例:<input v-model="text" />
1function useDebouncedRef(value, delay = 200) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) }, } }) } export default { setup() { return { text: useDebouncedRef('hello'), } }, }
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
类型定义:
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>
type CustomRefFactory<T> = (
track: () => void,
trigger: () => void
) => {
get: () => T
set: (value: T) => void
}
2
3
4
5
6
7
8
9
# shallowRef
创建一个 ref ,将会追踪它的 .value
更改操作,但是并不会对变更后的 .value 做响应式代理转换(即变更不会调用 reactive)。
const foo = shallowRef({})
// 更改对操作会触发响应
foo.value = {}
// 但上面新赋的这个对象并不会变为响应式对象
isReactive(foo.value) // false
2
3
4
5
# triggerRef
手动执行与 shallowRef
关联的任何效果。
const shallow = shallowRef({
greet: 'Hello, world',
})
// 第一次运行打印一次 "Hello, world"
watchEffect(() => {
console.log(shallow.value.greet)
})
// 这不会触发效果,因为ref是浅层的
shallow.value.greet = 'Hello, universe'
// 打印 "Hello, universe"
triggerRef(shallow)
2
3
4
5
6
7
8
9
10
11
12
13
14