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)
})
相关推荐
小陈同学呦7 小时前
前端如何处理订单状态导航的数据竞态问题
前端·javascript
开发者每周简报8 小时前
网海三部曲·无名宗师传
javascript·人工智能
喵个咪8 小时前
GoWind Toolkit 前端代码生成|Vue3(ElementPlus/Vben)、React(AntDesign)全自动一键生成教程
前端·vue.js·react.js
qq_2518364579 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
之歆10 小时前
Day01_ES6+ 专业指南:从基础到实战的现代JavaScript开发(下)
前端·javascript·es6
kyriewen10 小时前
AI生成代码快如闪电,但我修了三个小时——它到底帮了谁?
前端·javascript·ai编程
竹林81811 小时前
用 wagmi v2 和 viem 手写 NFT 市场批量上架功能,我踩遍了所有异步坑
javascript
zithern_juejin11 小时前
数组扁平化
javascript
清溪54912 小时前
n8n表达式沙箱逃逸至RCE漏洞-CVE-2025-68613复现
javascript·安全
Hilaku12 小时前
多标签页并发请求导致 Token 刷新失败?只有 15行代码就能解决 !
前端·javascript·程序员