首页
Preview

Vue 3 中的 Ref() 和 Reactive() — 该如何选择?

Composition API 中的 Ref 和 Reactive

Composition API 提供了两种方法来将响应式状态引入到组件中。因此,你需要决定是否使用 refreactive 或者两者都使用。我将帮助你做出正确的选择,但让我们先快速了解一下它们。

快速介绍

Ref()reactive() 用于跟踪其参数的更改。当你使用它们来初始化变量时,你向 Vue 提供了信息:“每当它们更改时,我希望你重新构建或重新评估依赖于这些变量的所有内容”。

在下面的示例中,单击按钮后,personRefpersonReactive 都将更改其名称,但普通的 JS 对象 person 不会更改。

<iframe src="https://medium.com/media/75114ab50d76a6127f4d830dd48c929b" allowfullscreen="" frameborder="0" height="633" width="680" title="ref_reactive_usage.vue" class="eh n ei rm bg" scrolling="no"></iframe>

它们的基本作用是使你的组件具有响应性(对更改做出反应)。

差异

你需要了解的主要差异有三个:

  • ref() 可以将原始值(最常见的是 BooleanStringNumber)以及对象作为参数,而 reactive() 只能将对象作为参数。

  • 但对于对象,两种语法都是有效的:

  • ref() 有一个 .value 属性,你必须使用它来获取其内容,但是对于 reactive(),你可以直接访问它:

    提示:当传递到模板时,ref 会被解封,这就是为什么你不需要在那里写 .value 的原因。

  • 使用 ref() 可以替换对象的整个实例,但是在使用 reactive() 时,你不能这样做:

它们都存在的原因是什么?

似乎 reactive() 只是 ref() 的一个有限版本,所以你是否应该考虑在代码中使用 reactive()?为什么不总是使用 ref()

我深入挖掘了 Vue 3 团队决定为我们提供使用 reactive() 的可能性并将其视为对 Vue 程序员不可用的内部对象的原因。

结果... 有一种情况下 reactive()ref() 更好。

转换视角

但在此之前,让我们看一下 Vue.js 源代码,以更好地理解响应性类之间的关系。在这里,你可以看到 Vue.js 3 中 ref() 实现的一部分:

function ref(value) {
  return createRef(value)
}

function createRef(rawValue, shallow = false) {
  if (isRef(rawValue)) {
    return rawValue
  }
  const value = shallow ? rawValue : convert(rawValue)
  const r = {
    _isRef: true,
    get value() {
      track(r, TrackOpTypes.GET, 'value')
      return value
    },
    set value(newVal) {
      if (hasChanged(newVal, rawValue)) {
        rawValue = newVal
        value = shallow ? newVal : convert(newVal)
        trigger(r, TriggerOpTypes.SET, 'value', newVal)
      }
    },
  }
  return r
}

function convert(value) {
  return isObject(value) ? reactive(value) : value
}

在第 8 行,你可以看到每次写 ref(x) 时都会调用一个构造函数。在其中,有趣的事情发生了。在第 10 行,调用了 toReactive(x),它只是一个将对象转换为 reactive() 的函数!

结论ref() 在内部使用 reactive(),你可以将 ref() 视为(几乎):

什么时候使用 reactive() 更好?

使用 reactive() 的一个理由是避免样板代码。在使用 ref() 时,你可能会厌烦在整个代码中写 .value。但是,如果需要原始值,你就不能避免使用 ref()... 除非你将它们放在对象中!

事实证明,当你想要在一个变量中拥有整个状态时,reactive() 很方便,就像在选项 API 中(在 data() 中)所做的那样。之所以如此,是因为 reactive() 跟踪每个属性,就像 ref() 一样。

这意味着,当 Vue 尝试确定是否应该更新依赖于它的任何内容时,reactive() 中的每个属性都被视为独立的 ref()

如果你想将 ref() 用作状态容器,每次更新一个属性时,使用状态的任何地方都会更新。这会触发不必要的重新渲染并降低应用程序的速度。

使用一个状态变量的示例:

const state = reactive({
  name: 'John',
  age: 25,
})

// bad practice:
const nameRef = ref(state.name)
const ageRef = ref(state.age)

// good practice:
const stateRef = reactive({
  name: 'John',
  age: 25,
})

结论:你可以将 reactive() 视为未解封的 ref() 容器,并在组件内部使用它,如果你想要一个状态变量。

总结

Ref()reactive() 在开始时可能看起来非常相似,但它们的目的有点不同。在这个主题上没有好的编程实践,所以一切都取决于你和你的团队如何决定。

在我看来,你应该在 ref()reactive() 中选择 ref()。这样代码会更加一致,你不需要考虑使用哪个,编码会稍微快一些。使用 .value 访问变量可能会让你感到不适,但我认为这表明变量具有响应性。使用 reactive() 时,我们失去了这些信息,必须检查变量定义(或内存)。

但是请记住,有一种特殊情况可以使 reactive() 成为你的好工具。如果你喜欢在组件内部使用一个变量来表示状态,那么它就是你的最佳选择。

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
皓月当空
名士风流,国士无双

评论(0)

添加评论