vue中ref()和reactive()的作用、区别、使用场景


vue的官网当中有详细的介绍,但描述的实在太官方了,你要读懂它估计要反复阅读好几遍
https://cn.vuejs.org/guide/essentials/reactivity-fundamentals.html#caveat-when-unwrapping-in-templates

ref() 函数和 reactive() 都是为了使一个属性变成响应式的,那么什么是响应式的,什么又是不响应式的呢?

响应式 和 非响应式

我个人理解,所谓响应式,就是一个地方变化了之后,其他依赖这个地方的地方也跟着相应的变化,这就是响应式的,反之就是不响应式的。
比如,页面上有好多个地方都引用了一个变量,要是我在某个时刻改变了这个变量的值,这时如果这个页面上引用这个变量的地方的都跟着变化了,那这就叫响应式的。

再比如

const a = ref(1)
const b = ref(1)
const c = computed(args => {
    return a.value + b.value
});
console.log(c.value); //此时输出2
a.value = 2;
console.log(c.value); //此时输出3

上面这个也叫响应式的,当然JavaScript语言本身也不是响应式的,这种写法是vue自己的特性,可以看到这里使用了一个 computed() 函数,这样定义的变量叫做计算属性,目的就是把一个变量变成响应式的。

let a = 1; //但是如果a不是响应式的
const b = ref(1)
const c = computed(args => {
    return a + b.value
});
console.log(c.value);
a = 2; //此时你在改变a的值。那么c也就不会跟着改变,因为a不是响应式的,a变了并不会引起依赖a的地方跟着改变
console.log(c.value);

这种性质在Java这种语言中是不可理解的。

int a=1;
int b=2;
int c=a+b
System.out.println(c) //此时输出2
a=2;
System.out.println(c) //此时输出2
//不可能在你改变a=2的时候,c自动变成3的,这在java如果不做特殊处理是不可能实现的。而java中要想实现这种特性,写法上就要复杂的多了

ref() 函数和 reactive()

我自己总结一下

在组合式 API 中,推荐使用 ref() 函数来声明响应式状态,ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回。这里用的是引用对象实现的,既然是引用,那意思就是说你现在拿到的是一个包含了原对象的新的对象,我明确告诉你你拿到的不是原来的对象哟,你想要使用的时候必须时刻记得是用.value的方式来操作原来的对象。

reactive()与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性,从而变成一个响应式对象,这个响应式对象是 JavaScript 代理对象,用的是代理的方式实现的,既然是代理,那意思就是表面上看是一模一样,但是骨子里还是有区别的,你要时刻注意,你拿到的其实已经不再是原来的对象了。

ref()

import {reactive, ref} from "vue";
// 如下有一个数组,使用 ref 来包装
let villagers = ref([]);
// 那么想要对这个数组进行操作的时候,你就必须使用villagers.value的形式来操作
villagers.value.push({});
villagers.value.pop();
villagers.value.filter(value=>{});

reactive()

import {reactive, ref} from "vue";
// 如下有一个数组,使用 reactive 来包装
let villagers = reactive([]);
// 那么想要对这个数组进行操作的时候,你就可以直接使用villagers来操作
villagers.push({});
villagers.pop();
villagers.filter(value=>{});

小例子

想要实现下面的效果

最终的代码如下,但是一开始的各种错误写法导致一直无法实现这样的效果,找原因找了好久,主要就是 ref() 和 reactive() 使用的问题. 这次彻底了解了一下这俩货的用途。

<template>
    <el-form :model="form" class="el-form">
        <el-form-item label="姓名">
            <el-input v-model="form.name" clearable @input="search"/>
        </el-form-item>
    </el-form>

    <el-table :data="villagers" style="width: 100%">
        <el-table-column label="姓名" prop="name"/>
        <el-table-column label="手机号" prop="phone"/>
        <el-table-column label="证件类型" prop="cardType"/>
        <el-table-column label="证件号码" prop="cardNo"/>
        <el-table-column label="卡号/账号" prop="account"/>
    </el-table>
</template>

<script setup>
import {reactive, ref} from "vue";

const villagerList = [{
    name: '孙士河',
    phone: '152****2966',
    cardType: '0100',
    cardNo: '131***********0217',
    account: '623************8261',
}, {
    name: '杨彦芳',
    phone: '158****2205',
    cardType: '0100',
    cardNo: '131***********0212',
    account: '623************4722',
}]
const villagers = ref(villagerList);

const form = reactive({
    name: ''
});
const search = () => {
    console.log('search:', form.name);
    villagers.value = villagerList.filter(villager => {
        return villager.name.includes(form.name);
    });
    // villagers.length = 0;
    // villagers.push(...villagers2);
}

</script>

<style>
.el-form {
    display: flex;
}
</style>

ref 实现

如果使用 ref()实现 则定义如下

const villagers = ref(villagerList);
const search = () => {
    console.log('search:', form.name);
    //这里可以直接让 villagers.value 指向一个新的对象,此时表格的数据也就跟着刷新了。
    //这时因为用ref定义的时候 villagers 变成了响应式的,然后表格上 :data="villagers" 这种写法其实是说只要你 villagers对应的值 变了,我表格就跟着变
    //那么此时villagers.value指向的引用变化了,相当于villagers也变化了,那么表格就跟着变化了。
    villagers.value = villagerList.filter(villager => {
        return villager.name.includes(form.name);
    });
}

错误写法

let villagers = ref(villagerList);
const search = () => {
    console.log('search:', form.name);
    //看上去像是villagers指向一个新的对象,但是此时表格并不会跟着变化
    //因为表格 :data="villagers" 这种写法其实是说只要你 villagers对应的值 变了,我表格就跟着变,此时villagers的指向变化了,但是villagers的value没有跟着变化,所以表格是不变化的
    villagers = villagerList.filter(villager => {
        return villager.name.includes(form.name);
    });
}

reactive 实现

let villagers = reactive(villagerList);
const search = () => {
    console.log('search:', form.name);
    const villagers2 = villagerList.filter(villager => {
        return villager.name.includes(form.name);
    });
    //先清空villagers数组的元素
    villagers.length = 0;
    //然后再把新元素加进去
    villagers.push(...villagers2);
}

错误写法1

let villagers = reactive(villagerList);
const search = () => {
    console.log('search:', form.name);
    const villagers2 = villagerList.filter(villager => {
        return villager.name.includes(form.name);
    });
    //先清空villagers数组的元素。 这里错在清空数组元素的方式,这里是把 villagers 指向了一个新的变量,虽然最后 villagers 值变了,但是
    //表格关联的响应式属性还是原来的那个,这里再怎么变化也无济于事,所以用 reactive 的话,一定要记得在原来的代理对象上操作,不要重新指向一个新的对象。这一点跟Java中的思想是一样的。
    villagers=[];
    //然后再把新元素加进去
    villagers.push(...villagers2);
}

错误写法2

let villagers = ref(villagerList);
const search = () => {
    console.log('search:', form.name);
    //这里跟错误写法1的原理是一样的,不要这样写
    villagers = villagerList.filter(villager => {
        return villager.name.includes(form.name);
    });
}

评论
  目录