1、为源响应式对象上的某个属性新创建一个ref
,保持响应式连接
<script setup lang="ts">
import { ref, Ref, reactive } from "vue"
const state = reactive({
foo: 1,
bar: 2,
})
const fooRef = ... // 修改这里的实现
// 修改引用将更新原引用
fooRef.value++
console.log(state.foo === 2)
// 修改原引用也会更新`ref`
state.foo++
console.log(fooRef.value === 3)
</script>
如上所述,...
的内容要写上什么,才能保持fooRef
和state.foo
的响应式连接。先试着写一个const fooRef = ref(state.foo)
,结果发现是不可行的,ref()
函数可以将原始值转换为响应式对象之外,但不能将源响应式对象的某个 property
转换为一个普通的响应式引用,从而保持引用和源对象的响应式连接。针对这个问题我们可以通过 toRef()
函数来实现。
<script setup lang="ts">
import { ref, Ref, reactive,toRef } from "vue"
const state = reactive({
foo: 1,
bar: 2,
})
const fooRef = toRef(state,'foo')
// 修改引用将更新原引用
fooRef.value++
console.log(state.foo === 2)
// 修改原引用也会更新`ref`
state.foo++
console.log(fooRef.value === 3)
</script>
要点:ref()
函数将原始值转换为包含一个 value
属性的响应式对象,而 toRef()
函数则可以将源响应式对象的某个 property
转换为一个普通的响应式引用。
2、解构/扩展对象,导致响应式丢失解决办法
<script setup lang="ts">
import { reactive } from "vue"
function useCount() {
const state = reactive({
count: 0,
})
function update(value: number) {
state.count = value
}
return {
state,
update,
}
}
// 确保解构不丢失响应性
const { state: { count }, update } = useCount()
</script>
<template>
<div>
<p>
<span @click="update(count-1)">-</span>
{{ count }}
<span @click="update(count+1)">+</span>
</p>
</div>
</template>
运行上述代码,我们会发现当我们点击更新方法,界面上的数值并不会随之改变,这是因为我们在解构之后丢失了响应式,这是因为解构赋值进行一次浅拷贝,而state
中的count
并不是引用类型,所以count
和state.count
并不是指向同一个地址,响应式也就失效了。
<script setup lang="ts">
import { reactive } from "vue"
function useCount() {
const state = reactive({
count: 0,
})
function update(value: number) {
state.count = value
}
return {
state:toRefs(state),
update,
}
}
// 确保解构不丢失响应性
const { state: { count }, update } = useCount()
</script>
<template>
<div>
<p>
<span @click="update(count-1)">-</span>
{{ count }}
<span @click="update(count+1)">+</span>
</p>
</div>
</template>
toRefs()
函数可以将响应式对象
转换为普通对象,但该普通对象中的每个属性都是一个单独的ref对象
。注意这个词哈对象
(引用类型),这个时候我们进行解构(浅拷贝)之后,指向的还是同一个地址哦,因此响应式也不会丢失了。
3、进一步了解一下toRef
和toRefs
这两个函数
简单概述:
toRef
的作用是将响应式对象的一个属性转化为一个 ref 对象;它接收两个参数,第一个参数是响应式对象,第二个参数是属性字符串,表示需要转化为 ref 对象的属性;
toRefs
的作用是将响应式对象转化为一个普通对象,并通过 toef
转化出每个属性的响应式引用。
注:两者都只能对响应式对象进行转换。toRefs
在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用 toRef
。另外,toRefs
返回的对象是响应式而不是深度嵌套响应式,只有对象自身的引用是响应式的。如果我们需要响应式地访问深度嵌套属性,还需要使用其他的方法来实现。
使用场景:
toRef
的使用场景经常出现在父子组件之间传递数据的过程中。例如,父组件传递了一个对象给子组件,并不想让子组件直接操作这个对象,可以将子组件需要的属性用 toRef
转化为 ref 对象,让子组件操作这个引用对象来达到修改父组件数据的目的。这样可以避免子组件直接修改父组件传递的数据,增加代码的可维护性和可读性。
toRefs
的使用场景经常出现在当从组合式函数中返回响应式对象时,使用它,消费者组件可以解构/展开返回的对象而不会失去响应性,这在我们自定义hook的时候经常能使用到。
最后看到这里的小伙伴,我推荐一个Vue.js挑战的在线编程网站,可用于查缺补漏,巩固相关知识,好东西,当然要分享啦。
[地址] cn-vuejs-challenges.netlify.app/