Skip to content

解答-2

仲灏约 1 分钟

解答-2

如何理解 ref toRef toRefs

细节的知识点,在整个 Composition API 中算是比较难理解的。 甚至出现了 ref sugar 的讨论。

ref 和 reactive 结合起来使用,更是难以理解。 既然有 reactive ,为何还要 ref 呢???

https://v3.cn.vuejs.org/api/refs-api.html

ref

创建响应式对象一般用 reactive

第一,响应式 (代码参考 RefBasic.vue

  • 生成值类型的响应式数据
  • 可直接用于模板和 reactive
  • 可通过 .value 修改值

第二,模板引用(代码参考 RefTemplate.vue

toRef

代码参考 ToRef.vue

可以用来为源响应式对象上的 property 新创建一个 ref。然后可以将 ref 传递出去,从而保持对其源 property 的响应式连接。

  • 针对一个响应式对象的 property
  • 创建一个 ref 具有响应式
  • 保持引用关系

【注意】必须是响应式对象,普通对象不具有响应式。

toRefs

代码参考 ToRefs.vue

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref 。

  • 响应式对象转换为普通对象 obj
  • obj 的每个属性都是对应的 ref
  • 保持引用关系

【注意】必须是响应式对象,普通对象不具有响应式。

当从合成函数返回响应式对象时,toRefs 非常有用。 这样消费组件就可以在不丢失响应性的情况下对返回的对象进行分解/扩散:

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

  // 逻辑运行状态,省略 N 行

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

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

    return {
      x,
      y
    }
  }
}

ref 和 reactive

既然有 reactive ,为何还要 ref 呢?

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

代码参考 ref/WhyRef.vue

为何需要 .value

  • 因为要保证响应式,需要把值类型封装成一个对象形式,所以用 .value 表示这个值
  • 模板,reactive 使用时,不需要 .value
  • 其他情况都需要 .value

结合 computed 的内部实现,可如下解释:

js
// 伪代码:不具有响应式,即改变值不会触发渲染
function computed(getter) {
  let value
  watchEffect(() => {
    value = getter() // value 是值类型,值拷贝,value 的变化不会传递到下游
  })
  return value
}

// 伪代码:具有响应式
function computed(getter) {
  const ref = {
    value: null,
  }
  watchEffect(() => {
    ref.value = getter() // ref 是引用类型,指针拷贝,ref 的变化会传递到下游
  })
  return ref
}

上述代码,可以把 watchEffect 改为 setTimeout 模拟一下

再聊 toRef 和 toRefs

  • 设计初衷:不丢失响应性的情况下对返回的对象进行分解/扩散
  • 必须存在:因为直接 property 或者 ...state ,会直接拿到属性的值。如果属性是值类型,则会丢失响应式 !!!(响应式是 Proxy 实现的)
  • 注意事项:不创造响应式,而是延续响应式 (所以针对普通对象不行,必须是响应式对象)

toRef toRefs 必须针对一个响应式对象,普通对象是无法变成响应式的。 它俩的设计初衷,本来也是针对响应式对象的 https://vue-composition-api-rfc.netlify.app/zh/api.html#toref 即,想要实现响应式,只有两个方法:ref reactive

  • ref 创造响应式
  • toReftoRefs 延续响应式

为何必须使用 toRef(state, 'name')toRefs(state) ??? 因为直接 property 或者 ...state ,会直接拿到属性的值。如果属性是值类型,则会丢失响应式。(响应式是 Proxy 实现的) 这也是引入 ref 的根源!!! 也是引入 Composition API ,抛弃 this ,带来的结果。

从使用角度

理解和应用要分开总结,这很重要。即便你听不懂原理,也要知道如何使用。

  • ref 值类型响应式
  • ref 模板引用
  • setup 返回时用 toRefs(state)
  • 用于第三方的逻辑函数,如 demo 中的 useMousePosition
  • 为所有的 ref 名加类似 xxxRef 的后缀

ref 和 reactive 混合起来,真的非常乱。 相比之下,React Hooks 只有 useState ,反而简单很多。—— 当然它也有其他的一些理解成本。

上次更新:

讨论区

欢迎留下想法与补充