shallowRef和shallowReactive的用法以及使用场景和ref和reactive的区别

Vue3 浅层响应式 API

1. ref vs shallowRef

1.1 基本概念

  • ref: 深层响应式,会递归地将对象的所有属性转换为响应式
  • shallowRef: 浅层响应式,只有 .value 的改变会触发更新,不会递归转换对象的属性

1.2 使用对比

js 复制代码
// ref 示例
const deepRef = ref({
  count: 0,
  nested: {
    value: 'hello'
  }
})

// 这些修改都会触发更新
deepRef.value.count++  // 触发更新
deepRef.value.nested.value = 'world'  // 触发更新

// shallowRef 示例
const shallowValue = shallowRef({
  count: 0,
  nested: {
    value: 'hello'
  }
})

// 只有直接替换 .value 才会触发更新
shallowValue.value.count++  // 不会触发更新
shallowValue.value.nested.value = 'world'  // 不会触发更新
shallowValue.value = { count: 1 }  // 触发更新

1.3 性能优化示例

vue 复制代码
<template>
  <div>
    <video ref="videoRef" :src="videoUrl"></video>
    <button @click="updateVideoUrl">更新视频</button>
  </div>
</template>

<script setup>
import { shallowRef } from 'vue'

// 使用 shallowRef 优化大型数据或 DOM 引用
const videoRef = shallowRef(null)
const videoUrl = shallowRef('https://example.com/video.mp4')

const updateVideoUrl = () => {
  // 直接更新 .value 触发更新
  videoUrl.value = 'https://example.com/new-video.mp4'
}
</script>

2. reactive vs shallowReactive

2.1 基本概念

  • reactive: 深层响应式,递归地将所有嵌套对象转换为响应式
  • shallowReactive: 浅层响应式,只将对象的第一层属性转换为响应式

2.2 使用对比

js 复制代码
// reactive 示例
const deepState = reactive({
  count: 0,
  nested: {
    value: 'hello'
  }
})

// 这些修改都会触发更新
deepState.count++  // 触发更新
deepState.nested.value = 'world'  // 触发更新

// shallowReactive 示例
const shallowState = shallowReactive({
  count: 0,
  nested: {
    value: 'hello'
  }
})

// 只有第一层属性的改变会触发更新
shallowState.count++  // 触发更新
shallowState.nested.value = 'world'  // 不会触发更新
shallowState.nested = { value: 'world' }  // 触发更新

2.3 实际应用示例

vue 复制代码
<template>
  <div>
    <h2>用户信息</h2>
    <div>姓名: {{ userInfo.name }}</div>
    <div>年龄: {{ userInfo.age }}</div>
    <!-- 不需要追踪 metadata 的变化 -->
    <div>元数据: {{ userInfo.metadata.lastUpdated }}</div>
  </div>
</template>

<script setup>
import { shallowReactive } from 'vue'

// 使用 shallowReactive 优化性能,metadata 的变化不需要触发更新
const userInfo = shallowReactive({
  name: 'John',
  age: 30,
  metadata: {
    lastUpdated: new Date(),
    visits: 0
  }
})

// 只有顶层属性的变化会触发更新
const updateUser = () => {
  userInfo.name = 'Jane'  // 触发更新
  userInfo.metadata.visits++  // 不会触发更新
}
</script>

3. 使用场景对比

3.1 适合使用深层响应式(ref/reactive)的场景

  1. 表单数据
js 复制代码
const formData = reactive({
  user: {
    name: '',
    email: '',
    preferences: {
      newsletter: true,
      notifications: {
        email: true,
        sms: false
      }
    }
  }
})
  1. 需要监听所有层级变化的数据
js 复制代码
const settings = ref({
  theme: {
    dark: false,
    colors: {
      primary: '#000',
      secondary: '#fff'
    }
  }
})

3.2 适合使用浅层响应式(shallowRef/shallowReactive)的场景

  1. 大型数据结构且只需要监听顶层变化
js 复制代码
const bigData = shallowRef({
  items: new Array(10000).fill(0).map((_, i) => ({
    id: i,
    data: { /* 大量数据 */ }
  }))
})

// 整体替换数据时才触发更新
const updateData = () => {
  bigData.value = newBigData
}
  1. 外部库或 DOM 引用
js 复制代码
const chartInstance = shallowRef(null)
const mapInstance = shallowRef(null)

onMounted(() => {
  chartInstance.value = new ThirdPartyChart()
  mapInstance.value = new ThirdPartyMap()
})
  1. 不需要深层响应式的状态管理
js 复制代码
const state = shallowReactive({
  ui: {
    loading: false,
    error: null
  },
  cache: new Map(), // 不需要响应式的缓存数据
  helpers: {
    formatter: () => {}, // 工具函数不需要响应式
  }
})

4. 性能优化建议

  1. 选择合适的响应式 API
js 复制代码
// ✅ 大型数据使用浅层响应式
const bigData = shallowRef(largeDataSet)

// ❌ 不必要的深层响应式
const bigData = ref(largeDataSet)
  1. 避免不必要的响应式转换
js 复制代码
// ✅ 静态数据使用浅层响应式
const config = shallowReactive({
  constants: { /* 大量静态配置 */ },
  settings: { /* 需要响应式的设置 */ }
})

// ❌ 对静态数据使用深层响应式
const config = reactive({
  constants: { /* 大量静态配置 */ },
  settings: { /* 需要响应式的设置 */ }
})
  1. 合理组合使用
js 复制代码
// 混合使用深层和浅层响应式
const state = reactive({
  // 需要深层响应式的数据
  userSettings: {
    theme: 'dark',
    notifications: { /* ... */ }
  },
  // 使用 shallowRef 包装大型数据
  bigData: shallowRef(largeDataSet)
})

5. 注意事项

  1. 响应式丢失问题
js 复制代码
const shallow = shallowReactive({
  nested: {
    count: 0
  }
})

// 解构会失去响应性
const { nested } = shallow
nested.count++ // 不会触发更新
  1. 替换整个对象
js 复制代码
const data = shallowRef({
  nested: {
    value: 0
  }
})

// 需要替换整个对象才能触发更新
data.value = {
  nested: {
    value: 1
  }
}
  1. 与计算属性配合
js 复制代码
// 当只需要监听部分数据变化时,使用计算属性
const data = shallowReactive({
  items: [],
  metadata: { /* ... */ }
})

// 只监听 items 的变化
const computedValue = computed(() => {
  return processItems(data.items)
})
相关推荐
轻口味2 分钟前
Vue.js Vuex 持久化存储(持久化插件)
vue.js
艾斯特_28 分钟前
Cursor使用及经验分享
前端·javascript·ai
大叔_爱编程1 小时前
wx043基于springboot+vue+uniapp的智慧物流小程序
vue.js·spring boot·小程序·uni-app·毕业设计·源码·课程设计
曾经的三心草2 小时前
小程序-模板与配置
前端·javascript·小程序
我命由我123452 小时前
VSCode 设置为中文(Configure Display Language)
前端·javascript·ide·笔记·vscode·学习·编辑器
无限大.3 小时前
用HTML、CSS和JavaScript实现庆祝2025蛇年大吉(附源码)
javascript·css·html
不停留8 小时前
贪心算法-跳跃游戏
前端·javascript·数据结构·算法·贪心算法
好奇的菜鸟11 小时前
Vue 3 中的响应式系统:ref 与 reactive 的对比与应用
前端·javascript·vue.js
Catherinemin13 小时前
剑指Offer|LCR 048.二叉树的序列化与反序列化
javascript·算法
Lilixxs13 小时前
开发环境搭建-3:配置 JavaScript 开发环境 (fnm+ nodejs + pnpm + nrm)
linux·运维·javascript·pnpm·fnm