-
Ref()和Reactive()是Vue 3 Composition API 中引入的创建响应式属性的新方法。
-
它们是包装对象,可以用内部值初始化并分配给变量。
-
在Vue 3中,我们需要先导入所需的包,然后再在组件中使用它。
-
我想你已经知道(_如何使用Vue CLI启动和运行Vue JS 3项目:这是一个链接)_了。
Ref()
-
我们可以像通常在设置函数中那样创建一个变量,并将其添加到返回的对象中。
-
然后在模板中渲染它。
-
这将会起作用,但没有响应性。
-
我们可以在不失去响应性的情况下创建一个属性,其中一个方法是使用ref()。
-
ref()对象接受一个内部值,并返回一个响应性和可改变的对象。
-
这对于原始类型的单一变量,如String、Boolean、Number等,是非常好的。
-
它有一个名为
.value
的单一属性,指向内部值,这就是我们如何获得和设置属性的值。
在顶部导入ref包
import { ref } from 'vue';
count变量持有一个内部值为0的ref()对象。
let count = ref(0);
ref()对象将有一个名为value
的单一属性,它指向内部值,在本例中是0。
为了获得或设置count变量的值,我们可以使用变量的属性.value
来解开它的值。
console.log(count.value); // 0 get
count.value = 12 // 12 set
然后,我们可以像下面这样通过返回setup()
函数来渲染计数变量。
正如你在下面的代码中注意到的,count
属性在模板中被渲染,而没有使用.value
属性。
这是因为当Ref对象被添加到setup
函数返回的对象中时,当我们在模板中使用它时,它会自动解除内部值的包装。
<template>
{{count}}
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let count = ref(0);
return {
count,
};
},
};
</script>
为了检查count
属性的反应性,我们给一个按钮元素附加一个点击事件。
然后我们给count
属性添加一个数字,以1为单位递增。
<template>
<div
style="
display: flex;
justify-content: center;
align-items: center;
height: 100vh;"
>
<el-button type="danger" @click="countNumber">Count</button>
<div>{{ count }}</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let count = ref(0);
function countNumber() {
count.value++;
}
return {
count,
countNumber
};
},
};
</script>
那么响应性和预期的一样。
Reactive()
reactive()也是一个封装对象,它接收一个对象并返回原对象的一个响应性代理。
它对字典结构的类型非常好,比如JS Object。
在顶部导入reactive包。
import { reactive } from 'vue';
这与ref对象非常相似,但内部的值应该是字典结构的数据,如JS对象,而不是一个单一的值。
let count = reactive({val: 0});
使用代理对象,我们可以像平时那样访问内部对象的属性。
console.log(count.val);
为了使这个对象具有响应性,我们所要做的就是在按钮点击事件的回调函数中把val属性递增1。
其余的都是一样的。
<script>
import { reactive } from "vue";
export default {
setup() {
let count = reactive({val:0});
function countNumber() {
count.val++;
}
return {
count,
countNumber,
};
},
};
</script>
这样就有了一个相同的结果。
好吧,我有一个作妖的想法,如果我对一个单一的内部值使用reactive呢?
先给出答案:该变量将失去其反应性。
当你用一个单值的reactive包装对象创建一个变量时,在浏览器的控制台会有一个警告:
value cannot be made reactive: 0
再来,反过来说,我如果用ref()而不是reactive()来处理JavaScript对象,它可以正常工作吗?
答案是肯定的。
我可以用ref()而不是reactive()来处理JavaScript对象,它可以正常工作,但在后台,**ref()求助于reactive(),**它将使用reactive包装对象来保持内部数据的响应性。
<script>
import { ref } from "vue";
export default {
setup() {
let count = ref({ val: 0 });
function countNumber() {
count.value.val++;
}
return {
count,
countNumber,
};
},
};
</script>
你可能会问......好吧,我可以用ref对象来创建我所有的变量,因为它们可以处理单值类型或复杂的数据类型。
这也行得通,但在获取或设置变量数据时,到处使用.value
属性会很繁琐。
(为了增加文章的趣味性和八卦性,在这儿说一个八卦:我们团队,包括架构都是满篇的ref
。我们是前端微服务,子项目很多,我也去看过其他子服务的前端代码,就发现有一个小哥是正常使用的,他是:单值类型的用ref,复杂的数据类型用reactive。但是包括我自己,从去年3.2一出来,就把vue3.2用在实际的大型项目上,在做响应式数据的时候,不管简单类型的数据,还是复杂类型的数据,都是用ref
,.value
漫天飞。不过到处使用.value
属性会特别繁琐,所以今年我就是:单值类型的用ref
,复杂的数据类型用reactive
)
为了限制到处使用.value
属性会很繁琐这一点,建议大家只对单值类型(如字符串、数字、布尔等)使用ref
。 在处理复杂的对象如对象、数组等时,使用reactive,
感觉是正确的。 还有我注意到的一点是,无论使用ref
还是reactive
,一个数组类型的变量都不会失去它的响应性,因为数组在 JavaScript上是一个对象。
使用异步数据
使用ref()
方法,我们可以很快做到这一点。
我使用setTimeout()
来模拟API调用。
<template>
<div
style="
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
"
>
<div>
ID: {{ user.id }} <br />
Name: {{ user.name }}
</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let user = ref({ id: 0, name: "小明" });
setTimeout(() => {
user.value = {
id: 20,
name: "xiaomingming",
};
}, 2000);
return {
user,
};
},
};
</script>
结果正如你在上面的例子中看到的,新的数据可以在2秒后呈现在DOM中,非常直接。 让我们看看如何使用reactive()
方法来完成这个任务。
<template>
<div
style="
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
"
>
<div>
ID: {{ user.id }} <br />
Name: {{ user.name }}
</div>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
let user = reactive({ id: 0, name: "小明" });
setTimeout(() => {
user = {
id: 20,
name: "xiaomingming",
};
}, 2000);
return {
user,
};
},
};
</script>
在上面的代码中,失去了对用户对象的响应性,因为我们不能直接改变整个对象。(这儿其实相当于我给用户对象user重新开辟了一个内存空间,改变了user的引用地址)
所以,相反,我们只能改变用户对象的属性。那么怎么办呢,怎么能又想使用reactive
又不是去对用户对象的响应性呢?
往下看。在下面的代码中,我单独声明了用户对象,然后使用用户属性将其添加到reactive()
方法的一个内部对象。 然后我可以很容易地在setTimeout()
函数中给该属性分配新的数据,该函数将在2秒后改变数据。
<template>
<div
style="
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
"
>
<div>
ID: {{ state.user.id }} <br />
Name: {{ state.user.name }}
</div>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
let user = { id: 0, name: "小明" };
let state = reactive({ user });
setTimeout(() => {
state.user = {
id: 20,
name: "xiaomingming",
};
}, 2000);
return {
state,
};
},
};
</script>
当然,你可能想说(或许你很乖,不说,哈哈哈),那你又另外声明了state,而且在模板里面,你也不是要通过 state.user.id
和 state.user.name
来将数据写入到模板吗?多了state.user.
,那如果属性多了,也不是state.user
漫天飞吗?
相信我,当业务场景多,业务逻辑复杂的时候,在模板里面多写几个 state.user.远比script里面到处使用.value属性会很简洁很多很多,而且在使用.value也会搞错哪些该加value,哪些不该加value.
总结
到这儿,总结一波~~
我希望这篇文章澄清了ref()
和reactive()
包装对象之间的一些关键区别,以及如何在Vue 3 Composition API中使用它们来创建变量而不失去其响应性。
我还向你展示了什么时候应该选择它们其中的一个。
如果你有任何问题,请随时在下面评论,我会尽快回复你。
最后:
Happy Coding!