渐层响应式shallowRef和shallowReactive
在vue3中,响应式系统可以让我们轻松的追踪到数据的变化。通常我们会使用ref和reactive来定义响应式数据;这两个API都是深度的,ref用来定义基本类型和对象类型的响应式数据,而reactive是用来定义对象类型的响应式数据的;如果因为性能原因我们并不想追踪很深的响应式的,这个时候我们就需要使用shallowRef和shallowReactive来对数据进行渐层的处理
1. 回顾ref和reactive
vue
<template>
<h2>ref姓名:{{ deepRef.user.name }}</h2>
<h2>ref年龄:{{ deepRef.user.age }}</h2>
<h2>reactive姓名:{{ deepReactive.user.name }}</h2>
<h2>reactive姓名:{{ deepReactive.user.age }}</h2>
<button @click="ChangeName">修改姓名</button>
<button @click="ChangeAge">修改年龄</button>
</template>
<script setup>
import { ref, reactive } from 'vue'
const deepRef = ref({ user: { name: 'Alice', age: 20 } })
const deepReactive = reactive({ user: { name: 'Bob', age: 25 } })
function ChangeName() {
deepRef.value.user.name = '张三'
}
function ChangeAge() {
deepReactive.user.age += 1
}
</script>

这样修改数据没有一点问题
2.shallowRef
- 如果更改成shallowRef则不会起作用
vue
const deepRef = shallowRef({ user: { name: 'Alice', age: 20 } })
这样点击并不会其任何作用,因为shallowRef只跟踪到deepRef.value,至于deepRef.value.user并不会被它追踪到,那么shallowRef到底有什么作用呢?
什么时候使用shallowRef?
当你有一个大型的、嵌套非常深的对象且你只需关心这个对象整体有没有被替换,不关心内部到底那里更换了,使用它可以消除很深的性能开销;常于markRaw配合使用,跳过某些大对象的响应式转换,例如
- 比如我们有一个配置文件,这个配置文件类似json一样有一大堆数据,我们并不需要修改数据中某一项,只要修改就是整体替换,避免修改某一项导致配置文件失效出错
vue
<template>
<div>
<pre>{{ config }}</pre>
<button @click="updateConfig">更新配置</button>
</div>
</template>
<script setup>
import { shallowRef } from 'vue';
const config = shallowRef({
theme: 'dark',
layout: {
header: true,
footer: false
}
})
function updateConfig() {
// 整体替换,触发更新
config.value = {
...config.value,
theme: 'light',
layout: { header: false, footer: true }
}
}
</script>
<style></style>

3.shallowReactive
这个也是类似shallowRef,当时响应式对象里面还有一个对象,里面的对象就并不能深度追踪到了
vue
<template>
<div>
<p>计数:{{ state.count }}</p>
<p>用户:{{ state.user.name }}</p>
<button @click="increment">增加 count</button>
<button @click="changeUserName">修改用户名</button>
</div>
</template>
<script setup>
import { shallowReactive } from 'vue';
const state = shallowReactive({
count: 0,
user: { name: 'Alice' } // user 不是响应式的
})
function increment() {
state.count++ // 会触发更新
}
function changeUserName() {
state.user.name = 'Bob' // 不会触发更新,界面上的用户名不会改变
// 但如果想要更新,可以整体替换 user 对象:
// state.user = { name: 'Bob' } // 这会触发更新,因为替换了顶层属性
}
</script>

什么时候使用shallowReactive?
比如你有一个对象,只希望顶层是响应式的,而内部嵌套不需要被追踪时;例如一个表单对象,里面包含了和上面一样含有复杂的配置对象,这个配置永远都不需要变动,或者它只是只读的;、
实际使用中我们可以使用markRaw来标记某个对象永远都不会是响应式的
vue
import { shallowRef, markRaw } from 'vue'
const hugeObject = markRaw({ /* 巨大的静态数据 */ })
const state = shallowRef(hugeObject)
// 现在 state.value 中的任何属性变化都不会触发响应式更新
// 只有整体替换 state.value 时才会更新