ref和reactive是什么?
Vue3中的ref和reactive都是响应式的数据类型,它们都可以用来创建响应式的数据,
1、ref :创建基本类型的响应式数据
作用:定义响应式变量。
语法:const xxx = ref(初始值)
例如:const computerTemp = ref(35)
返回值:一个RefImpl
的实例对象,简称ref对象
或ref
,ref
对象的value
属性是响应式的。
<template>
<div class="person">
<h2>室内温度:{{ computerTemp }} ℃</h2>
<button @click="changecomputerTemp">修改室内温度</button>
</div>
</template>
<script setup name="HelloWorld">
import { ref } from 'vue'
let computerTemp = ref(35)
function changecomputerTemp() {
computerTemp.value += 2
console.log('computerTemp~~', computerTemp)
}
</script>
【注意】:
- 在js中操作数据时需要:
xxx.value
,但模板中不需要.value
,直接使用即可; - 对于
const computerTemp = ref(35)
来说,computerTemp
不是响应式的,computerTemp .value
是响应式的。 - 从控制台打印结果可以看出:computerTemp是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的;
- JS中操作ref对象时候必须要.value computerTemp.value是响应式的
2、ref :创建对象类型的响应式数据
ref
接收的数据可以是:基本类型、对象类型。- 若
ref
接收的是对象类型,内部其实也是调用了reactive
函数。
<template>
<div class="person">
<h2>个人信息:姓名{{ personInfo.name }},age{{ personInfo.age }}岁 <button @click="changeAge">age+1</button></h2>
<h2>作息表:</h2>
<h3 v-for="item in table" :key="item.id">{{ item.name }}</h3>
<button @click="changeName">click me</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { ref } from 'vue'
let personInfo = ref({ name: '菜鸟', age: 18 })
let table = ref([
{ id: '001', name: '吃早餐' },
{ id: '002', name: '吃午餐' },
{ id: '003', name: '吃晚餐' }
])
function changeAge() {
personInfo.value.age += 10
console.log('personInfo~~', personInfo)
}
function changeName() {
table.value[0].name = '跑步'
table.value[1].name = '午休'
table.value[2].name = '学习'
console.log('table~~', table);
}
</script>
3、reactive :创建对象类型的响应式数据
作用:定义一个响应式对象(基本类型不要用它,要用ref
,否则报错)
语法:const 对象名= reactive({key:value…})
例如:const infoObj = reactive({ name: ‘菜鸟’, age: 18 })
返回值:一个Proxy
的实例对象,简称:响应式对象。
<template>
<div class="person">
<h2>个人信息:</h2>
<span>name:{{infoObj.name}}</span>
<span>age:{{infoObj.age}}</span>
<button @click="changeInfo">修改信息</button>
</div>
</template>
<script setup name="HelloWorld">
import { ref,reactive } from 'vue'
const infoObj = reactive({ name: '菜鸟', age: 18 })
function changeInfo() {
infoObj.age += 1
infoObj.name = 'cainiao'
console.log('infoObj~~', infoObj)
}
</script>
【注意】:
reactive
定义的响应式数据是“深层次”的;
let deepObj = reactive({
a:{
b:{
c:{
d:{
x:666
}
}
}
}
})
function testDeep(){
deepObj.a.b.c.d.x = 888
console.log('deep~~',deepObj.a.b.c.d.x);
}
4、ref和reactive的区别
ref
用来定义:基本类型数据、对象类型数据;reactive
用来定义:对象类型数据。ref
创建的变量必须使用.value
(可以使用volar
插件自动添加.value
)。reactive
重新分配一个新对象,会失去响应式(可以使用Object.assign
去整体替换)。
【以下是reactive 使用不当会失去响应】
1、赋值一个普通对象
let state = reactive({ count: 0 })
// 这个赋值将导致 state 失去响应
state = { count: 1 }
2、赋值一个 reactive 对象
<template>
{{ state }}
</template>
<script setup>
const state = reactive({ count: 0 })
// 在 nextTick 异步方法中修改 state 的值
nextTick(() => {
// 并不会触发修改 DOM ,说明失去响应了
state = reactive({ count: 11 });
});
</script>
3、将 reactive 对象的属性赋值给变量(断开连接/深拷贝)
这种操作类似于深拷贝,不再共享同一内存地址,而是只是字面量的赋值,对该变量的赋值不会影响原来对象的属性值。
let state = reactive({ count: 0 })
// 赋值给 n,n 和 state.count 不再共享响应性连接
let n = state.count
// 不影响原始的 state
n++
console.log(state.count) // 0
4、直接 reactive 对象解构时
let state = reactive({ count: 0 })
// 普通解构,count 和 state.count 失去了响应性连接
let { count } = state
count++ // state.count 值依旧是 0
解决方法:
1、不要直接整个对象替换,一个个属性赋值
let state = reactive({ count: 0 })
state.count = 1
2、使用 Object.assign
let state = reactive({ count: 0 })
state = Object.assign(state, { count: 1 })
3、避免将 reactive 对象的属性赋值给变量。
4、使用 toRefs 解构,解构后的属性是 ref 的响应式变量。
const state = reactive({ count: 0 })
// 使用 toRefs 解构,后的属性为 ref 的响应式变量
let { count } = toRefs(state)
count.value++ // state.count 值改变为 1
5. 使用场景选择:
- 对于简单数据类型,如数值、字符串等,必须使用ref。
- 对于一个响应式对象,层级不深,
ref
、reactive
都可以 - 对于复杂对象类型,建议使用reactive。