vue3提供的compositionAPI语法中关于ref、reactive、toRefs、toRef的使用方法

237 阅读4分钟

什么是响应式?

我理解的响应式就是,当数据进行变化时,视图会进行相应的更新。

先来回顾一下之前我们定义响应式数据的方式,是这样的:

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语法中关于响应式的部分内容。