创建响应式的对象,只能传入对象,是创建一个该对象的Proxy,他就是原对象的一个响应式副本,不等于原始对象,它“深层”转换了源对象的所有嵌套 property,解包并维持其中的任何ref引用关系。
reactive API很好地解决了 Vue2 通过 defineProperty 实现数据响应式时的缺陷。
就也是创建响应式对象,但是任何数据类型都可以作为参数,将参数包裹成一个具有value 属性 的对象返回,然后使用ref对象.value访问或者更改数据,----因为基础数据类型只能传递值而不是引用地址,将它包装在一个对象内,可以实现数据的响应式。
因为ref就是通过reactive包装了一个对象 ,然后将值传给该对象的value属性,这也就是为什么每次访问时我们都需要加上.value。可以简单地把 ref(obj) 理解为 reactive({ value: obj })。
如何选择
ref和reactive?建议:
- 基础类型值(
String、Number、Boolean等) 或单值对象(类似{ count: 3 }这样只有一个属性值的对象) 使用ref- 引用类型值(
Object、Array)使用reactive- 对于 ref 对象可以使用 unref 语法糖来免去
.value访问的困扰
传入对象的某一个属性创建一个响应式的ref, 它会保持对其源 property 的响应式连接。
<template>
<p>ref state1: {{ state1.count }}</p>
<button @click="add1">增加 state1 的 count</button>
<p>toRef state2: {{ state2 }}</p>
<button @click="add2">增加 state2</button>
</template>
<script>
import { ref, toRef } from 'vue'
export default {
setup() {
const obj = { count: 3 }
const state1 = ref(obj)
// const state1 = ref(obj.count) // 如果传简单数据类型是值传递,无法体现对源数据的响应式
const state2 = toRef(obj, 'count')
function add1() {
state1.value.count ++
console.log('原始值:', obj); // obj的 count 递增
console.log('响应式数据对象:', state1.value.count);
console.log('state2', state2.value)
// state1 的 count 属性 和 state 2 递增,页面上的 state1 和 state2 也递增
}
function add2() {
state2.value ++
console.log('原始值:', obj); // obj 的 count 递增
console.log('响应式数据对象:', state2.value);
console.log('state1', state1.value.count)
// state2 的值 和 state1 的 count 属性也同步递增,页面上的 state1 和 state2无变化
}
return { state1, state2, add1, add2 }
}
}
</script>
得出结论:
ref 创建一个响应式对象,如果传入参数是对象,那么与对象所有嵌套属性都维持数据响应。它的作用是 data 选项 般的存在,即组件内部状态。ref值改变会触发页面渲染,同时能作为props或 事件参数 进行组件通信。
toRef 是对传入对象指定属性的响应式绑定,值改变不会更新视图。因此用它创建的变量不作用于模版渲染,而是用来接收诸如props的引用式传递。举例来说:通常就是我们在模板中只想要渲染出obj里面的一个属性,如果直接返回该值,那么就会失去响应式,因此使用toRef既能只抛出一个属性且保持响应式。
使用toRef过程中,会发现,toRef只能代理一个对象属性。使用toRef可以代理这个对象下的所有属性。
了解完 toRef 后,就很好理解 toRefs 了,其作用是生成一个新对象,内部每个属性都指向传入的对象的相应 property 的响应式数据 ref。
也就是说,新对象本身与原对象的无关联(指向新的引用地址),但它的所有属性却都与源对象的对应属性建立了响应性。
toRef 可以记成建立一个 ref 属性值的引用,toRefs 则是所有 ref 响应属性值的引用。
如果直接对响应式数据解构,那么数据就会失去响应性,所以就需要toRefs建立响应式关联,一般都是根据key进行解构
如果某个属性不存在,这种情况下toRefs不会为不存在的属性创建ref,此时就需要使用toRef
ref、reactive是在setup()声明组件内部状态用的, 这些变量通常都要 return 出去,除了供或渲染函数渲染视图,也可以作为 props 或 emit 参数 在组件间传递。它们的值变更可触发页面渲染。toRef、toRefs主要用于处理 组件/函数 传递的响应式数据,譬如在接收父组件props时 / 或composables组合式函数返回数据时 建立起某些属性的响应式引用。ref包装的属性在 setup 函数内都需要通过.value去访问它值 (template 模版内不用)。因此,ref、toRef创建的变量值都需要用变量.value读取。reactive则不用,因为会自动解包分配给它的ref。toRefs**,如果是解构赋值,如const { state1, state2 } = toRefs(props),值需要这样获取:state1.value.count;const state = toRefs(props),则是state.state1.value。toRefs可以解构。props 或 事件 传递时,均会维持其响应性