Composition API 中的 Ref 和 Reactive
Composition API 提供了两种方法来将响应式状态引入到组件中。因此,你需要决定是否使用 ref
、reactive
或者两者都使用。我将帮助你做出正确的选择,但让我们先快速了解一下它们。
快速介绍
Ref()
和 reactive()
用于跟踪其参数的更改。当你使用它们来初始化变量时,你向 Vue 提供了信息:“每当它们更改时,我希望你重新构建或重新评估依赖于这些变量的所有内容”。
在下面的示例中,单击按钮后,personRef
和 personReactive
都将更改其名称,但普通的 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()
可以将原始值(最常见的是Boolean
、String
和Number
)以及对象作为参数,而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()
成为你的好工具。如果你喜欢在组件内部使用一个变量来表示状态,那么它就是你的最佳选择。
评论(0)