Skip to content

为什么需要用 ref、toRef 和 toRefs

深入理解 ref、toRef 和 toRefs

为何需要 ref ?

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

例子

js
<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,其他情况都需要

例子

js
<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

js
// 模拟 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封装的)非普通对象
  • 注意:不创造响应式,而是延续响应式

例子

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

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

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

基于 MIT 许可发布