ref本质也是reactive,ref(obj)等价于reactive({value: obj})
vue3中实现响应式数据的方法是就是使用ref和reactive,所谓响应式就是界面和数据同步,能实现实时更新
vue2中响应式是通过defineProperty实现的,vue3中是通过ES6的Proxy来实现的
一、reactive
1、概念
reactive的参数必须是一个对象,包括json数据和数组都可以,否则不具有响应式
如果给reactive传递了其他对象(如时间对象),默认情况下修改对象界面不会自动更新,如果想更新,可以通过给对象重新赋值来解决。
2、使用
<template>
<div>
{{ info.name }} -- {{ info.age }}
<input type="text" onChange="inputChange" />
</div>
</template>
<script setup>
// reactive属于vue3 新增的组合式api之一 需要从vue中引入
import { reactive } from "vue";
/**
* 使用 reactive 定义 响应式数据
* info 改变数据会重新渲染
*
* 注意:不能把整个info的值替换
*/
const info = reactive({ name: "张三", age: 18 });
/**
*
* 每次输入框变化,修改info对象里面的name属性
*/
const inputChange = (event) => {
/**
* 修改后页面会重新渲染
* 注意:这里的数据流向和之前一致
* 数据修改是同步,视图渲染是异步
*/
info.name = event.target.value;
};
</script>
二、ref(实战用的较多)
1、概念
ref的参数可以是基本数据类型,也可以是引用数据类型。ref会把参数加工成一个响应式对象。如果使用的是基本类型响应式依赖Object.defineProperty( ),如果ref使用的是引用类型,底层ref会借助reactive的proxy 定义响应式。
2、基本使用
<template>
<div>
<!-- 在template里 可以直接使用ref定义的数据 -->
{{ num }}
<button @click="add">+1</button>
</div>
</template>
<script setup>
import { ref } from "vue";
/**
* 使用 ref 对象把 传入的基本数据类型包装成一个对象
*
* 返回的num的值 是一个对象
*/
const num = ref(1);
const add = () => {
/**
* ref 的值 挂在在对象的value属性
*
* 注意:不能整个修改num的值
*/
num.value += 1
}
</script>
ref和reactive都属于递归监听,也就是数据的每一层都是响应式的,如果数据量比较大,非常消耗性能,非递归监听只会监听数据的第一层
三、shallowRef和shallowReactive
ref和reactive定义的数据每一层都是响应式数据,使用shallowRef和shallowReactive后只有第一层数据具备响应式。语法和ref和reactive一致
1、shallowRef+triggerRef
<template>
<div>
{{ infos.name }}
<button @click="updateChildName">修改子集</button>
{{ num }}
<button @click="add">+1</button>
</div>
</template>
<script setup>
import { shallowRef, triggerRef } from "vue";
const infos = shallowRef({
name: "张三",
});
const num = shallowRef(1);
const updateChildName = () => {
// 这一行视图不会改变
infos.value.name = "李四";
/**
* 如果用了shallowRef 还想让界面刷新可以使用
* 传入要更新的数据
* 🤔 看场景使用,一般情况下没有太大必要
*/
triggerRef(infos)
};
const add = () => {
// 可以正常触发
num.value += 1
}
</script>
2、shallowReactive
注意:shallowReactive没有类似triggerRef()的方法
<template>
<div>
{{ infos.name }} -- {{ infos.child.name }}
<button @click="updateChildName">修改第一层</button>
{{ num }}
<button @click="updateChildName2">修改第二层</button>
</div>
</template>
<script setup>
import { shallowReactive } from "vue";
const infos = shallowReactive({
name: "张三",
child: {
name: "张三的儿子",
},
});
const updateChildName = () => {
// 正常改变
infos.name = "李四";
};
const updateChildName2 = () => {
// 无法修改
infos.child.name = "李四的儿子";
};
</script>
三、toRaw
1、介绍
有些时候我们不希望数据进行响应式实时更新,可以通过toRaw获取ref或reactive引用的原始数据,通过修改原始数据,不会造成界面的更新,只有通过修改ref和reactive包装后的数据时才会发生界面响应式变化。
2、使用
<template>
<div>
<button @click="update">+1</button>
{{ info.name }}
<button @click="rawUpdate">取消响应式修改</button>
</div>
</template>
<script setup>
import { reactive, toRaw } from "vue";
const info = reactive({
name: "张三",
});
const update = () => {
info.name = info.name + 1;
};
const rawUpdate = () => {
/**
* 取消响应式
* 注意:toRaw 只能用作引用数据类型
*/
const a = toRaw(info);
// 后续修改失去作用
a.name = '李四'
};
</script>
四、markRaw(了解)
markRaw包装后的数据永远不会被追踪。暂时没发现有啥用,看个热闹即可
五、toRef
toRef 是对定义的响应对象的某个属性进行引用
<template>
<div>
{{ info.name }} --- {{ info.age }}
<button @click="updateName">修改名字</button>
</div>
</template>
<script setup>
import { ref, toRef } from "vue";
const info = {
name: "张三",
age: 18,
};
/**
* 从info对象中取出name属性
* 并加上响应式
*
* 返回的nameRef 的 value属性的值 就是 name的值
*/
const nameRef = toRef(info, "name");
const updateName = () => {
// 把name改成李四
nameRef.value = '李四'
}
</script>
六、toRefs
遍历对象中的所有属性,将其变为响应式数据,这是因为toRef只能传一个key,toRefs所达到的效果与toRef一样
以上最常用的是ref 和 reactive 其它 看个热闹即可
const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
foo: Ref<number>,
bar: Ref<number>
}
*/
// 这个 ref 和源属性已经"链接上了"
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3