toRef 和 toRefs的作用:
toRef: 基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。通俗来说,就是复制 reactive 里的单个属性并转成 ref,改变该属性的值将更新ref的值,反之,改变复制后的ref的值,对应的reactive里的属性的值也会改变,类似浅拷贝;
toRef应用:想要将响应式对象中的某个属性单独提供给外部使用。
toRefs: 将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用toRef()创建的。通俗来说,就是复制 reactive 里的所有属性并转成 ref,改变某属性的值将更新对应ref的值,反之,改变复制后的ref的值,对应的reactive里的属性的值也会改变;
总结:toRef复制的是reactive 里的单个属性,而toRefs复制的是reactive 里的所有属性。
toRef使用方式:
const state = reactive({
foo: 1,
bar: 2
})
// 复制 reactive 里的单个属性,转成 ref
const fooRef = toRef(state, 'foo')
// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2
console.log(fooRef.value) // 2
// 更改源属性也会更新该 ref
state.foo++
console.log(state.foo) // 3
console.log(fooRef.value) // 3
toRef()就相当于一个中间件,将属性转换成响应式数据,但一次只能处理一个;
注意:toRef不同于ref:
const fooRef = ref(state.foo);
上面这个 ref 不会和 state.foo 保持同步,因为这个 ref() 接收到的是一个纯数值。
toRef() 这个函数在你想把一个 prop 的 ref 传递给一个组合式函数时会很有用:(上文提到过的toRef应用)
<script setup>
import { toRef } from 'vue'
const props = defineProps(/* ... */)
// 将 `props.foo` 转换为 ref,然后传入
// 一个组合式函数
useSomeFeature(toRef(props, 'foo'))
</script>
当 toRef 与组件 props 结合使用时,关于禁止对 props 做出更改的限制依然有效。尝试将新的值传递给 ref 等效于尝试直接更改 props,这是不允许的。在这种场景下,你可能可以考虑使用带有 get 和 set 的 computed 替代。
即使源属性当前不存在,toRef() 也会返回一个可用的 ref。这让它在处理可选 props 的时候格外实用,相比之下 toRefs 就不会为可选 props 创建对应的 refs。
toRefs使用方式:
const state = reactive({
foo: 1,
bar: 2
})
// 复制 reactive 里的所有属性
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
可以看出,功能与toRef一致,但是可以批量处理多个ref对象。
当从组合式函数中返回响应式对象时,toRefs相当有用。使用它,消费者组件可以解构/展开返回的对象而不会失去响应性:
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2
})
// ...基于状态的操作逻辑
// 在返回时都转为 ref
return toRefs(state)
}
// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()
toRefs在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用toRef。
最后注意:这两个属性作用是一样的,但是访问方式不同
template 要想访问 toRefs 的值,需要带上 .value 如果不带上,就会出现双引号。
template 要想访问 toRef 的值,不需要带上 .value
<template>
<h2>
reactive-toRef: {{ info.msg }}
reactive-toRefs: {{ info1.msg }}
</h2>
<h2>
// 不需要带上 .value
toRef: {{ msgCopy }}
</h2>
<h2>
// 需要带上 .value
toRefs: {{ infoCopy.msg.value }}
</h2>
<button @click="onChangeMsg">更换 toRef</button>
<button @click="onChangeInfo">更换 toRefs</button>
</template>
<script>
import { reactive, toRef, toRefs } from 'vue'
export default {
setup() {
let info = reactive({
name: 'Tony',
msg: 'Hello'
})
let info1 = reactive({
name: 'Tony1',
msg: 'Hello1'
})
// 复制 info 里的 msg 属性 针对单个属性
let msgCopy = toRef(info, 'msg')
// 复制整个info 针对所有属性
let infoCopy = toRef(info1);
// 更改 msgCopy
const onChangeMsg = () => {
msgCopy.value = 'world!'
}
// 更改 infoCopy.msg
const onChangeInfo = () => {
infoCopy.msg.value = 'world1!'
}
return {
info,
info1,
msgCopy,
infoCopy,
onChangeMsg,
onChangeInfo
}
}
}
</script>