跳到主要内容

为什么需要用 ref、toRef 和 toRefs

深入理解 ref、toRef 和 toRefs

为何需要 ref ?

  • 返回值类型,会丢失响应式
  • 如在 setup、computed、合成函数,都有可能返回值类型
  • Vue 如不定义 ref,用户将自造 ref,反而混乱

例子

<template>
<p> why ref demo {{ age }}</p>
</template>

<script>
import {reactive, ref, toRefs} from "vue";

function useFeatureX() {
const state = reactive({
x: 1,
y: 2
})

return toRefs(state)
}

export default {
name: "WhyRef",
setup() {
// vue3 是通过 proxy 实现响应式,所以值类型不具备响应式
const {x, y} = useFeatureX()
let age = ref(20)

console.log(age)
// RefImpl {_shallow: false, dep: undefined, __v_isRef: true, _rawValue: 20, _value: 20}
// dep: Set(1) {ReactiveEffect}
// __v_isRef: true
// _rawValue: 20
// _shallow: false
// _value: 20
// value: 30

setTimeout(() => {
age.value = 30
}, 1500)

return {
age
}
}
}
</script>

为何需要 .value ?

  • ref 是一个对象(不丢失响应式),value 存储值
  • 通过 .value 属性的 get 和 set 实现响应式
  • 用于模板、reactive 时,不需要 .value,其他情况都需要

例子

<template>
<p> why .value demo {{ state.age }} - {{ age1 }}</p>
</template>

<script>
import {computed, reactive, ref, toRefs} from "vue";

function useFeatureX() {
const state = reactive({
x: 1,
y: 2
})

return toRefs(state)
}

export default {
name: "WhyValue",
setup() {
// vue3 是通过 proxy 实现响应式,所以值类型不具备响应式
const {x, y} = useFeatureX()
const state = reactive({
name: 'lzw.',
age: 20
})

// computed 返回类似 ref 的对象,也有 .value
const age1 = computed(() => {
return state.age + 1
})

console.log(age1)
// ComputedRefImpl {dep: undefined, _dirty: true, __v_isRef: true, effect: ReactiveEffect, _setter: ƒ, …}
// dep: Set(1) {ReactiveEffect}
// effect: ReactiveEffect {active: true, deps: Array(1), fn: ƒ, scheduler: ƒ}
// __v_isReadonly: true
// __v_isRef: true
// _dirty: false
// _setter: () => {…}
// _value: 36
// value: 36

setTimeout(() => {
state.age = 35
}, 1500)

return {
state,
age1
}
}
}
</script>

通过模拟 computed,为啥需要 .value

// 模拟 computed,为啥需要 .value
function computed1(getter) {
let value
setTimeout(() => {
value = getter()
}, 1000)
return value
}

const a1 = computed1(() => 100)
console.log(a1) // undefined

function computed2(getter) {
const ref = {
value: null
}
setTimeout(() => {
ref.value = getter()
}, 1000)
return ref
}

const a2 = computed2(() => 100)
console.log(a2) // {value: null}value: 100[[Prototype]]: Object

为何需要 toRef 和 toRefs

  • 初衷:不丢失响应式的情况下,把对象数据 分解/扩散/解构
  • 前提:针对的是响应式对象(reactive封装的)非普通对象
  • 注意:不创造响应式,而是延续响应式

例子

// 定义函数
function useFeatureX() {
const state = reactive({
x: 1,
y: 2
})

// 返回时转为 ref
return toRefs(state)
}

export default {
setup() {
// 可以在不失去响应性的情况下破坏结构
const {x, y} = useFeatureX()
return {x, y}
}
}