ref()
和 reactive()
都是响应式api,用来跟踪其参数的更改,让组件具有响应性。
1. ref和reactive的区别
1.1 接收值不同
ref
ref 可以处理基础类型的值,也可以处理引用类型的值。
如果给 ref 函数传递了一个引用类型的值,如传递一个对象,那么这个对象就会通过 reactive() 这个方法将其转换成具有深层次的响应式对象,也就是说这种情况下ref 内部本质还是调用了 reactive 方法。
const ref1 = ref(0) // OK
const ref2 = ref({ count: 0 }) // OK
reactive
reactive 仅处理引用类型的值,不允许传递基础类型的值。
reactive若传入基础类型的值,对该基础类型数据是没有响应式追踪能力的。
const reactive1 = reactive(0) // NOT OK
const reactive2 = reactive({ count: 0 }) // OK
1.2. 数据访问方式不同
ref
对于 ref 来说,无论是原始类型还是对象,访问数据都需要通过 .value 的形式进行,更新数据也需要通过 .value 来更新。
但在<template>中使用ref的值时不需要带上.value。
const ref1 = ref(0)
console.log(ref1.value) // 0
const ref2 = ref({ count: 0 })
console.log(ref2.value.count) // 0
ref1.value = 1
console.log(ref1.value) // 1
<template>
<div>{{ ref1 }}</div>
</template>
<script setup>
import { ref } from 'vue'
const ref1 = ref(0)
</script>
reactive
reactive是 proxy 代理的对象数据,可以直接获取到数据,不必添加 .value。
看到ref取值和reactive的取值方式不同,可以看出ref本质就是基于reactive的二次包装。
const reactive1 = reactive({ num: 0})
console.log(reactive1.num) // 0
1.3. watch监听方式不同
ref
可以直接监听 ref 的数据,当 ref 的数据发生变化的时候,就会执行 watch 函数对应的回调。
const ref1 = ref(0)
watch(ref1, () => {
console.log('changed!')
})
当然这里只是原始类型数据,如果是对象的话,需要深度监听 deep: true。
const ref1 = ref({num: 1})
watch(ref1, () => {
console.log('changed!')
})
// ref1.value.num = 1
// 执行该语句时并不会触发watch监听,watch 并没有对ref1进行深度监听
// 但注意,此时dom是能更新的,ref会将其转换成 reactive 的形式
// 要想深入监听,只需要加一个对应的参数即可
const ref1 = ref({num: 1})
watch(ref1, () => {
console.log('changed!')
}, { deep: true })
reactive
reactive 因为本质是对象,所以在 watch 的时候本能的会添加 deep 属性。
vue 对其做了优化watch 监听 reactive 的时候可以不添加 deep 属也能够对其做深度监听。
const reactive1 = reactive({num: 1})
watch(reactive1, () => {
console.log('changed!')
})
// reactive1.num = 1
// 触发watch监听
备注
对比 ref,reactive 会跟踪每个属性的变动。
这意味着在 reactive 中的每个属性都会被视为独立的 ref ,Vue 会据此确定是否需要更新某些依赖于这些属性的内容。
如果想使用ref 作为状态容器,那么每当一个属性更新时,使用该状态的任何地方都将被更新。这会触发不必要的重新渲染并减慢应用程序的速度。
2. ref、toRef、toRefs的区别
ref
复制,修改响应式数据不影响以前的数据。
toRef
引用,修改响应式数据会影响以前的数据。
toRefs
1. 接收一个对象作为参数,它会遍历对象身上所有属性,然后调用单个toRef。
2. 将对象的多个属性变成响应式数据,并且要求响应式数据和原始数据关联。
3. 通过typeof判断ref和reactive类型
3.1. ref 类型判断
ref 关键字声明一个变量时,变量的类型会被推断为 Ref 类型,无论变量本身是什么类型的值,typeof 判断为Object。
可以使用 unref 函数来解包得到ref 对象的实际值,再对unref()进行判断。
let num = ref(123)
console.log(typeof num) //Object
console.log(typeof unref(num)) //Number
3.2.reactive类型判断
reactive用来声明复杂数据类型时,typeof判断为Object。
let myName = reactive({name:'min'})
console.log(typeof myName) //Object
console.log(typeof myName.name) //String
疑惑
reactive用来声明基础数据类型时,typeof判断按理说会判断为Object(被reactive包装了)
但试验发现仍会判断为原本的数据类型
但总而言之是不推荐用reactive包裹基础数据类型的
let obj = reactive(123)
console.log(typeof obj) //number