Vue3 shallowRef 和 shallowReactive

本文详细介绍了Vue3中的shallowRef和shallowReactive,这两个函数用于创建浅层响应式数据。区别于ref和reactive的深度监听,shallowRef处理基本类型数据,仅监听.value变化;shallowReactive只处理第一层数据,仅监听数据的第一层。通过案例展示了它们在处理多层数据时的区别,强调在不需要深度监听的情况下使用这两个函数可以提高性能。

Vue3 shallowRef 和 shallowReactive

嗯,怎么说呢,其实这两个函数并不是很常用,在开发过程中基本上用不到,但是呢,我不写我又感觉少点啥,所以说就简单的说一下吧,其实不看也可以哈。

shallowRef 和 shallowReactive

  • shallowRef 函数,只处理基本类型数据。
  • shallowReactive 函数,只处理第一层数据。
  • 两个在使用的时候都需要引入才可以。

上面说了是不是还是没看懂?没关系哈,先记住上面三条,然后详细的说一下。

我们在之前的博客讲过 ref 函数和 reactive 函数,他们的作用是将数据转换成响应式的数据,在修改数据的时候,可以将数据实时展示在页面上,基本数据也好,对象也好,都是这样。

但是有一个问题呀,我们在把数据改为响应式数据的时候,不管是用 ref 函数还是使用 reactive 函数,他俩都是深度监听,啥意思呢?就是 reactive 包裹的对象,就算有100层,也就是连续点一百个属性那种,去修改最深层的数据,也是可以监听到的,这样的话就会存在问题了。

深度监听的问题:

  • 无论 ref 函数还是 reactive 函数都是深度监听。
  • 如果数据量过大,超级超级消耗性能。
  • 如果我们不需要对数据进行深度监听的时候,就可以使用 shallowRef 函数和 shallowReactive 函数。

明白了吗?不明白没关系,我们通过几个案例就知道了。

使用 shallowReactive 非深度监听

记住一点,shallowReactive 函数,只能处理第一层数据

假设我们页面有一个个人信息展示,有名字、有年龄需要展示,我们数据是存在 boy 对象里面,然后 age 是在 boy 对象的 news 属性下面,也就是深层,但是 name 是在 boy 对象下面,也就是第一层,我们有两个按钮,分别修改 name 和 age,看一下会有什么效果。

<template>
  <div>
    <h1>姓名:{{name}}</h1>
    <h1>年龄:{{news.age}}</h1>
    <button @click="btn">修改name</button>
    <button @click="btn2">修改age</button>
  </div>
</template>

<script>
import { shallowReactive, toRefs } from "vue";
export default {
  name: "App",
  setup() {
    const boy = shallowReactive({
      name: "我是𝒆𝒅.",
      news: {
        birthday: "2012-10-14",
        age: 10
      }
    });

    const btn = () => {
      boy.name = "𝒆𝒅.";
    };

    const btn2 = () => {
      boy.news.age++;
    };

    return { ...toRefs(boy), btn, btn2 };
  }
};
</script>

我们分别点击两个按钮,看一下页面变化。

在这里插入图片描述

通过效果,我们稍微总结一下:

  • shallowReactive只会包装第一层的数据
  • 默认情况它只能够监听数据的第一层。
  • 如果想更改多层的数据,必须先更改第一层的数据,然后在去更改其他层的数据。这样视图上的数据才会发生变化。

使用 shallowRef 非深度监听

开头已经说过了,shallowRef 函数,只能处理基本类型数据,为啥呢,因为 shallowRef 函数监听的是.value 变化。并不是第一层的数据的变化。所以如果要更改 shallowRef 创建的数据,应该 xxx.value = XXX。

看代码:

<template>
  <div>
    <h1>姓名:{{boy}}</h1>
    <button @click="btn">修改boy</button>
  </div>
</template>

<script>
import { shallowRef } from "vue";
export default {
  name: "App",
  setup() {
    const boy = shallowRef("我是𝒆𝒅.");

    const btn = () => {
      boy.value = "𝒆𝒅.";
    };

    return { boy, btn };
  }
};
</script>

点击按钮,修改 boy 的值。

在这里插入图片描述

通过上面的截图可以看见,数据是可以正常修改的。

然后遗留了一个问题:shallowRef 函数,只处理基本类型数据 吗?

看下面的案例:

<template>
  <div>
    <h1>姓名:{{boy.name}}</h1>
    <h1>年龄:{{boy.news.age}}</h1>
    <button @click="btn">修改name</button>
    <button @click="btn2">修改age</button>
  </div>
</template>

<script>
import { shallowRef } from "vue";
export default {
  name: "App",
  setup() {
    const boy = shallowRef({
      name: "我是𝒆𝒅.",
      news: {
        birthday: "2012-10-14",
        age: 10
      }
    });

    const btn = () => {
      boy.value.name = "𝒆𝒅.";
    };

    const btn2 = () => {
      boy.value.news.age++;
    };

    return { boy, btn, btn2 };
  }
};
</script>

在这个代码里面,我们用 shallowRef 包裹了一个对象, 然后在页面显示 name 和 age ,然后我们通过按钮修改 name 和 age,看一下页面的效果。

在这里插入图片描述

所以说呢,shallowRef 函数,只能处理基本类型数据

好了,今天的内容大体就这么多,其实这两个函数不怎么用,知道就好,万一用到也好明白怎么用。

今天就到这里了,再见了各位宝子!

<think> 我们详细讲解Vue3中的refreactiveshallowRefshallowReactive。这些API都是用于创建响应式数据的。 1. ref: - 作用:创建一个响应式的数据对象,可以包装基本类型或引用类型。 - 原理:对于基本类型,ref通过Object.defineProperty的getset实现响应式;对于引用类型,内部会调用reactive来实现深层响应式。 - 使用方法:通过`ref(value)`创建,在模板中直接使用,在JS中需要通过`.value`访问。 - 特点:每次修改都会触发响应式更新。 2. reactive: - 作用:创建一个响应式的对象(只能用于对象、数组等引用类型)。 - 原理:使用Proxy代理对象,实现深层响应式。 - 使用方法:通过`reactive(object)`创建,可以直接访问修改属性,无需.value。 - 特点:对嵌套对象也进行深度响应式转换。 3. shallowRef: - 作用:创建一个响应式数据,但只对顶层属性进行响应式处理。如果值是对象,不会对内部属性进行深层响应式转换。 - 原理:只监听.value的变化,不监听内部属性的变化。 - 使用方法:同ref,通过`.value`访问修改。 - 场景:当有一个大型对象,但只关心整体替换而不关心内部属性变化时使用。 4. shallowReactive: - 作用:创建一个浅层响应式对象,只对顶层属性进行响应式处理,嵌套属性不会转换为响应式。 - 原理:只对对象的第一层属性进行响应式处理。 - 使用方法:同reactive,直接访问修改属性。 - 场景:当只需要对象的第一层属性响应式,而嵌套对象不需要响应式时使用。 下面我们通过代码示例具体说明: ```vue <template> <div> <h2>ref 示例</h2> <p>count: {{ count }}</p> <button @click="count++">增加count</button> <p>state: {{ state.count }}</p> <button @click="state.count++">增加state.count</button> <h2>reactive 示例</h2> <p>reactiveState.count: {{ reactiveState.count }}</p> <button @click="reactiveState.count++">增加reactiveState.count</button> <p>reactiveState.nested.count: {{ reactiveState.nested.count }}</p> <button @click="reactiveState.nested.count++">增加reactiveState.nested.count</button> <h2>shallowRef 示例</h2> <p>shallowState: {{ shallowState }}</p> <button @click="shallowState.count++">增加shallowState.count(不会更新视图)</button> <button @click="replaceShallowState">整体替换shallowState(会更新)</button> <h2>shallowReactive 示例</h2> <p>shallowReactiveState.count: {{ shallowReactiveState.count }}</p> <button @click="shallowReactiveState.count++">增加shallowReactiveState.count(会更新)</button> <p>shallowReactiveState.nested.count: {{ shallowReactiveState.nested.count }}</p> <button @click="shallowReactiveState.nested.count++">增加shallowReactiveState.nested.count(不会更新视图)</button> </div> </template> <script> import { ref, reactive, shallowRef, shallowReactive } from &#39;vue&#39;; export default { setup() { // ref 示例 const count = ref(0); // 基本类型 const state = ref({ count: 0 }); // 引用类型,内部会调用reactive // reactive 示例 const reactiveState = reactive({ count: 0, nested: { count: 0 } }); // shallowRef 示例 const shallowState = shallowRef({ count: 0 }); // shallowReactive 示例 const shallowReactiveState = shallowReactive({ count: 0, nested: { count: 0 } }); const replaceShallowState = () => { // 替换整个值才会触发更新 shallowState.value = { count: Math.random() }; }; return { count, state, reactiveState, shallowState, shallowReactiveState, replaceShallowState }; } }; </script> ``` ### 代码说明: 1. **ref**: - `count`是一个基本类型,通过`.value`修改,模板中直接使用`count`(不需要.value)。 - `state`是一个对象,通过`state.value.count`修改,但在模板中我们直接绑定`state`(因为模板会自动解包ref的顶层属性)。 2. **reactive**: - `reactiveState`是一个响应式对象,无论是顶层属性`count`还是嵌套属性`nested.count`,修改都会触发更新。 3. **shallowRef**: - 修改`shallowState.value.count`不会触发更新,因为shallowRef不会对内部属性进行响应式处理。 - 只有替换整个`shallowState.value`(即使用一个新对象)才会触发更新。 4. **shallowReactive**: - 修改顶层属性`shallowReactiveState.count`会触发更新。 - 修改嵌套属性`shallowReactiveState.nested.count`不会触发更新,因为shallowReactive只对顶层属性进行响应式处理。 ### 总结: - **ref**:常用于基本类型,也可以用于引用类型。在JS中需要`.value`访问,模板中自动解包。深层响应式。 - **reactive**:用于对象、数组等引用类型,无需`.value`,深层响应式。 - **shallowRef**:只关心整体替换,不关心内部属性变化时使用。需要`.value`访问,内部属性非响应式。 - **shallowReactive**:只需要对象顶层属性响应式时使用。嵌套属性非响应式。 根据实际需求选择: - 如果需要深层响应式,使用refreactive。 - 如果只需要顶层响应式(性能优化),使用shallowRefshallowReactive。 注意:使用shallowRef时,如果内部嵌套了reactive对象,那么修改这个reactive对象的属性是会触发更新的,因为reactive对象本身是响应式的。但是shallowRef不会追踪内部属性的变化,它只追踪整个.value的变化。 §§相关问题§§: 1.Vue3中,refreactive的主要区别是什么? 2. 为什么有时候我们需要使用shallowRefshallowReactive3. 当使用ref包装一个对象时,Vue内部是如何处理的? 4. 如何判断一个对象是ref对象还是reactive对象? </think> ### 详细讲解 `ref`、`reactive`、`shallowRef`、`shallowReactive` #### 1. **ref** **作用**:创建响应式数据引用(支持基本类型对象)。 **原理**: - 通过 `value` 属性访问/修改数据 - 对象类型内部自动转换为 `reactive` - 使用 `Object.defineProperty` 实现响应式 **API**: ```javascript import { ref } from &#39;vue&#39; const count = ref(0) // 基本类型 const user = ref({ name: &#39;Alice&#39; }) // 对象类型自动转为 reactive // 修改值 count.value++ user.value.name = &#39;Bob&#39; ``` **模板中使用**: ```vue <template> <div>{{ count }}</div> <button @click="count++">Increment</button> </template> ``` --- #### 2. **reactive** **作用**:创建深层响应式对象(仅支持对象/数组)。 **原理**: - 基于 `Proxy` 实现深度响应式 - 嵌套对象自动转为 `reactive` - 直接访问属性(无需 `.value`) **API**: ```javascript import { reactive } from &#39;vue&#39; const state = reactive({ count: 0, user: { name: &#39;Alice&#39; } // 嵌套对象自动响应式 }) // 修改值 state.count++ state.user.name = &#39;Bob&#39; ``` **模板中使用**: ```vue <template> <div>{{ state.count }}</div> <button @click="state.count++">Increment</button> </template> ``` --- #### 3. **shallowRef** **作用**:创建浅层响应式引用(仅顶层 `value` 响应)。 **原理**: - 只追踪 `.value` 变化(不追踪内部属性) - 对象内部修改 **不会** 触发更新 - 替换整个 `.value` **会** 触发更新 **API**: ```javascript import { shallowRef } from &#39;vue&#39; const user = shallowRef({ name: &#39;Alice&#39; }) // ❌ 内部修改无效(不会触发更新) user.value.name = &#39;Bob&#39; // ✅ 替换整个对象有效(触发更新) user.value = { name: &#39;Bob&#39; } ``` **典型场景**: - 大型不可变数据(如三方库实例) - 性能敏感场景避免深度监听 --- #### 4. **shallowReactive** **作用**:创建浅层响应式对象(仅顶层属性响应)。 **原理**: - 只响应顶层属性变化 - 嵌套对象 **不会** 自动转为响应式 **API**: ```javascript import { shallowReactive } from &#39;vue&#39; const state = shallowReactive({ count: 0, user: { name: &#39;Alice&#39; } // 嵌套对象非响应式 }) // ✅ 顶层修改有效 state.count++ // ❌ 嵌套修改无效(不会触发更新) state.user.name = &#39;Bob&#39; ``` **典型场景**: - 表单分组(只关心组变化) - 性能优化(避免深度监听大对象) --- ### 总结对比表 | 特性 | ref | reactive | shallowRef | shallowReactive | |---------------------|-------------------|------------------|------------------|------------------| | **支持类型** | 所有类型 | 对象/数组 | 所有类型 | 对象/数组 | | **深层响应式** | ✅ (对象自动转) | ✅ | ❌ | ❌ | | **响应触发条件** | .value 变化 | 属性修改 | .value 替换 | 顶层属性修改 | | **访问方式** | 需要 `.value` | 直接访问 | 需要 `.value` | 直接访问 | | **性能开销** | 中等 | 较高(深度监听) | 低 | 低 | | **典型场景** | 基本类型/对象引用 | 复杂状态对象 | 大型不可变数据 | 层级明确的数据 | ### 代码示例总结 ```vue <script setup> import { ref, reactive, shallowRef, shallowReactive } from &#39;vue&#39; // 1. ref(深度响应) const deepRef = ref({ count: 0 }) deepRef.value.count++ // 触发更新 // 2. reactive(深度响应) const deepReactive = reactive({ user: { name: &#39;Alice&#39; } }) deepReactive.user.name = &#39;Bob&#39; // 触发更新 // 3. shallowRef(浅层响应) const shallow = shallowRef({ count: 0 }) shallow.value.count++ // ❌ 不触发更新 shallow.value = { count: 1 } // ✅ 触发更新 // 4. shallowReactive(浅层响应) const shallowState = shallowReactive({ user: { name: &#39;Alice&#39; } }) shallowState.user.name = &#39;Bob&#39; // ❌ 不触发更新 shallowState.user = { name: &#39;Bob&#39; } // ✅ 触发更新 </script> ``` ### 选择建议 - 用 **`ref`** 处理基本类型或需要整体替换的对象 - 用 **`reactive`** 处理深度嵌套的复杂状态 - 用 **`shallowXXX`** 优化大型数据或明确不需要深度监听的场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我 + V

感谢!代码知味,赏续新篇。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值