reactive
本身具有很大局限性导致使用过程需要额外注意,如果忽视这些问题将对开发造成不小的麻烦;ref更像是vue2时代option api
的data
的替代,可以存放任何数据类型,而reactive
声明的数据类型只能是对象;
先抛出结论,再详细说原因:非必要不用reactive
! (官方文档也有对应的推荐)
官方原文:建议使用 ref()
作为声明响应式状态的主要 API。
最懂Vue的人都这么说了:推荐ref!!!!!!
reactive
和 ref
对比
reactive | ref |
---|---|
❌只支持对象和数组(引用数据类型) | ✅支持基本数据类型+引用数据类型 |
✅在 <script> 和 <template> 中无差别使用 | ❌在 <script> 和 <template> 使用方式不同(script中要.value ) |
❌重新分配一个新对象会丢失响应性 | ✅重新分配一个新对象不会失去响应 |
能直接访问属性 | 需要使用 .value 访问属性 |
❌将对象传入函数时,失去响应 | ✅传入函数时,不会失去响应 |
❌解构时会丢失响应性,需使用toRefs | ❌解构对象时会丢失响应性,需使用toRefs |
-
ref 用于将基本类型的数据(如字符串、数字,布尔值等)和引用数据类型(对象) 转换为响应式数据。使用 ref 定义的数据可以通过
.value
属性访问和修改。 -
reactive 用于将对象转换为响应式数据,包括复杂的嵌套对象和数组。使用 reactive 定义的数据可以直接访问和修改属性。
原因1:reactive
有限的值类型
reactive只能声明引用数据类型(对象)
let obj = reactive({
name: '小明',
age : 18
})
ref既能声明基本数据类型,也能声明对象和数组;
Vue 提供了一个 `ref()`[1] 方法来允许我们创建可以使用任何值类型的响应式 ref
const state = ref({})
const state2 = ref([])
原因2:reactive
使用不当会失去响应:
reactive
一时爽,使用不恰当的时候失去响应泪两行,开开心心敲代码过程中,会感叹!!咦?怎么不行?为什么这么赋值失去响应了? 辣鸡reactive!!! 我要用 ref 👉👉yyds
1. 给reactive赋一整个普通对象/reactive对象
通常在页面数据回显时,需要将AJAX请求获取的对象直接赋值给响应式对象,如果操作不当就导致
reactive声明的对象
失去响应
-
赋值一个普通对象
let state = reactive({ count: 0 }) state = {count: 1}
-
赋值一个
reactive
对象如果给reactive的响应式对象赋值普通对象会失去响应,那么给它赋值一个reactive的响应式对象不就行了吗?下面试试看
<template>
{{state}}
</template>
<stcirpt setup>
const state = reactive({ count: 0 })
//nextTick异步方法中修改state的值
nextTick(() => {
//并不会触发修改DOM ,说明失去响应了
state = reactive({ count: 11 });
});
</stcirpt>
在nexTick
中给state
赋值一个reactive的响应式对象,但是DOM并没有更新!
解决方法:
-
不要直接整个对象替换,对象属性一个个赋值
let state = reactive({ count: 0 }) state.conut = 1
-
使用
Object.assign
let state = reactive({ count: 0 }) state = Object.assign(state , {count:1})
-
使用ref定义对象
非必要不用reactive
let state = ref({ count: 0 }) state.value={count:1}
为什么同样是赋值对象ref不会失去响应而reactive会?
ref
定义的数据(包括对象)时,返回的对象是一个包装过的简单值,而不是原始值的引用;
就和对象深拷贝一样,是将对象属性值的赋值
reactive
定义数据(必须是对象),reactive
返回的对象是对原始对象的引用,而不是简单值的包装。
类似对象的浅拷贝,是保存对象的栈地址,无论值怎么变还是指向原来的对象的堆地址;
reactive
就算赋值一个新的对象,reactive
还是指向原来对象堆地址
2.将reactive对象的属性-赋值给变量(断开连接/深拷贝)
这种类似深拷贝不共享同一内存地址了,只是字面量的赋值;对该变量赋值也不会影响原来对象的属性值
let state = reactive({ count: 0 })
let n = state.count
n++
console.log(state.count)
有人就说了,既然赋值对象的属性,那我赋值一整个对象不就是浅拷贝了吗?那不就是上面说的给响应式对象的字面量赋一整个普通对象/reactive对象
这种情况吗?这种是会失去响应的
3.直接reactive对象解构时
-
直接解构会失去响应
let state = reactive({ count: 0 })
let { count } = state
count++
解决方案:
-
使用
toRefs
解构不会失去响应使用toRefs解构后的属性是
ref
的响应式数据
const state = reactive({ count: 0 })
let { count } = toRefs(state)
count.value++
建议: ref一把梭
当使用reactive时,如果不了解reactive失去响应的情况,那么使用reactive会造成很多困扰!
推荐使用ref
总结原因如下:
-
reactive有限的值类型:只能声明引用数据类型(对象/数组)
-
reactive在一些情况下会失去响应,这个情况会导致数据回显失去响应(数据改了,dom没更新)
给响应式对象的字面量赋一整个普通对象,将会导致reactive声明的响应式数据失去响应
<template> {{state.a}} {{state.b}} {{state.c}} </template> <script> let state = reactive({ a:1,b:2,c:3 }) onMounted(()=>{ state = { a:11,b:22,c:333 } state.a = 11 state.b = 22 state.c = 33 }) </script>
上面这个例子如果是使用ref进行声明,直接赋值即可,不需要将属性拆分一个个赋值
使用ref替代reactive:
<template> {{state.a}} {{state.b}} {{state.c}} </template> <script> let state = ref({ a:1,b:2,c:3 }) onMounted(()=>{ state.value = { a:11,b:22,c:333 } }) </script>
-
ref适用范围更大,声明的数据类型.基本数据类型和引用数据类型都行
虽然使用ref声明的变量,在读取和修改时都需要加.value
小尾巴,但是正因为是这个小尾巴,我们review代码的时候就很清楚知道这是一个ref
声明的响应式数据;
ref的.value
小尾巴好麻烦!
ref声明的响应式变量携带迷人的.value
小尾巴,让我们一眼就能确定它是一个响应式变量!虽然使用ref声明的变量,在读取和修改时都需要加.value
小尾巴,但是正因为是这个小尾巴,我们review代码的时候就很清楚知道这是一个ref声明的响应式数据;
可能有些人不喜欢这个迷人小尾巴,如果我能自动补全阁下又如何应对?
volar插件能自动补全.value
(强烈推荐!!!!!!!)
本人推荐ref一把梭,但是ref又得到处
.value
,那就交给插件来完成吧!!!
-
valor
自动补全.value
(不是默认开启,需要手动开启) -
不会有人不知道Vue3需要不能使用
vetur
要用valor
替代吧?不会不会吧? (必备volar插件)
可以看到当输入ref声明的响应式变量时,volar插件自动填充.value
那还有啥烦恼呢? 方便!
本文会根据各位的提问和留言持续更新;
@ 别骂了_我真的不懂vue 说(总结挺好的,因此摘抄了):
reactive 重新赋值丢失响应是因为引用地址变了,被proxy代理的对象已经不是原来那个所以丢失响应了,其实ref也是一样的,当把.value那一层替换成另外一个有着.value的对象也会丢失响应 ref定义的属性等价于reactive({value:xxx})
另外说使用Object.assign为什么可以更新模板
Object.assign解释是这样的: 如果目标对象与源对象具有相同的键(属性名),则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的同名属性。
那个解决方法里不用重新赋值,直接Object.assign(state,{count:1})即可,所以只要proxy代理的引用地址没变,就会一直存在响应性
参考资料
[1]
https://cn.vuejs.org/api/reactivity-core.html#ref: https://link.juejin.cn/?target=https%3A%2F%2Fcn.vuejs.org%2Fapi%2Freactivity-core.html%23ref