什么是响应式?
我理解的响应式就是,当数据进行变化时,视图会进行相应的更新。
先来回顾一下之前我们定义响应式数据的方式,是这样的:
data() {
return {
name:'maxine'
}
}
现如今我们可以使用compositionAPI的reactive和ref来使数据具备响应式,那么reactive和ref的区别是什么呢?
ref
-
先来说ref,ref是用来处理基础类型的数据的
我们先来看这个代码
<template>
<div class="root">{{name}}</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
setup() {
let name='maxine'
setTimeout(() => {
name='ell'
},2000)
return { name };
},
});
</script>
上述代码,在setup中有一个变量name,我们希望通过一个定时器实现两秒后name的值'maxine'变为'ell',但是实际情况就是两秒后页面内容没有任何变化。原因就是name不是一个响应式的变量。这个时候我们就可以使用一下ref来看看运行结果是否可以像预想中一样实现。
<template>
<div>{{name}}</div>
</template>
<script>
import { defineComponent, ref } from 'vue@3.3.4';
export default defineComponent({
setup() {
let name = ref('maxine');
setTimeout(() => {
name.value='ell'
},2000)
return { name };
},
});
</script>
结果像预期一样,页面首先显示 maxine ,两秒后显示为 ell。
因为,ref能够使数据实现响应式的引用,其实他的原理就是通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新。
也就是说let name = ref('maxine');这行代码,在'maxine'前加了ref之后,proxy就会把'maxine'变成proxy( { value: 'maxine' ) }这样的一个响应式引用。所以在定时器中我们就可以通过name.value='ell'对数据进行更新。
但是在template模板中为什么我们使用name而不使用name.value呢?
原因就是vue在做模板处理时会做一个转化,在模板中写name时,他如果知道name是一个由ref返回的一个响应式的引用的话,他会自动的帮我们在底层调用name.value,所以我们就没必要自己再写name.value了。
reactive
-
reactive是用来处理非基础类型的数据
<template>
<div>{{nameObj.name}}</div>
</template>
<script>
import { defineComponent, reactive } from 'vue@3.3.4';
export default defineComponent({
setup() {
const nameObj = reactive({name:'maxine'});
setTimeout(() => {
nameObj.name = 'ell'
}, 2000);
return {nameObj}
}
});
</script>
普通的对象本身也是没有响应式的特性,这个时候我们就可以使用reactive去处理对象或者数组这种非基础数据类型的数据,使其变成响应式数据,reactive呢,是通过proxy把 name:'maxine' 变成 proxy( { name: 'maxine' } )
那么问题来了,如果我通过添加下边这行代码把name解构出来,是不是在模版中就可以不用再nameObj.name,而是直接使用name了呢?
const { name } = nameObj;
return { name }
答案是,不是的,原因就是reactive通过proxy使{ name: 'maxine' }这个对象具备了响应式特性,但是我们通过解构拿到对象中具体的数据的话,也就是具体name的值的话,这个值只是一个普通的字符串,是不具备响应式特性的,如果想要解决这个问题的话,怎么做呢?
toRefs
vue还提供了一个语法叫做toRefs
<template>
<div>{{name}}</div>
</template>
<script>
import { defineComponent, reactive, toRefs } from 'vue@3.3.4';
export default defineComponent({
setup() {
const nameObj = reactive({name:'maxine'});
setTimeout(() => {
nameObj.name = 'ell'
}, 2000);
const { name } = toRefs(nameObj);
return {name}
}
});
</script>
通过toRefs解构出nameObj中的name,使name具备响应式。
toRefs的原理,nameObj本来是 proxy( { name: 'maxine' } ),使用toRefs之后他就变成了
{ name: proxy( { value: 'maxine' } ) },这个时候模板中调用的name其实就是name.value
所以说toRefs的作用其实就是,把一个reactive返回的对象转化成ref的一个形式。
那如果我这样写呢?
<template>
<div>{{age}}</div>
</template>
<script>
import { defineComponent, reactive, toRefs } from 'vue@3.3.4';
export default defineComponent({
setup() {
const nameObj = reactive({name:'maxine'});
setTimeout(() => {
nameObj.age = '11'
}, 2000);
const { age } = toRefs(nameObj);
return {age}
}
});
</script>
我通过toRefs去解构nameObj中不存在的属性,那么这个age会生效吗?
如果会生效的话,一开始这个age应该为空,然后变成11对吧?但是结果是控制台返回给我们一个错误,说value找不到。这就说明toRefs这个语法在解构nameObj对象中不存在的属性时,不会给这个属性一个默认的响应式引用,而是一个undefined。这样我们在上述的写法就不会生效。
怎么解决这个问题呢?
toRef
使用toRef
<template>
<div>{{age}}</div>
</template>
<script>
import { defineComponent, reactive, toRef } from 'vue@3.3.4';
export default defineComponent({
setup() {
const nameObj = reactive({name:'maxine'});
setTimeout(() => {
nameObj.age = '11'
}, 2000);
const age = toRef(nameObj,'age');
return {age}
}
});
</script>
const age = toRef(nameObj,'age');
这句代码的意思就是,我们从nameObj中解构age,如果age不存在我们就给他一个默认的空值,这样即便age不存在,那么现在生成的这个新的变量age他也是具备响应式的一个空值,这样给他赋值就依然具备响应式特性。这就是toRef的一个意义和作用,但是一般不提倡使用。
以上就是vue3提供的compositionAPI语法中关于响应式的部分内容。