当前位置: 首页 > news >正文

Vue 3 理解 ref, reactive, computed, watch

今天的目标非常明确:彻底搞懂 Vue 3 响应式系统的四大基石。只要掌握了这四个 API,你就掌握了 Vue 3 数据流的核心。

我们将通过概念解析 + 代码对比 + 避坑指南的方式,确保你不仅“会用”,还能“懂原理”。


1. ref:万物皆可 Ref

核心定义:用于定义响应式变量。它可以包裹基本数据类型(String, Number, Boolean),也可以包裹对象或数组。

💡 关键规则

  • JS 中访问:必须加 .value(例如 count.value)。
  • 模板中访问自动解包,不需要 .value(例如 {{ count }})。
  • 适用场景首选方案。无论是数字还是对象,用 ref 最统一,重构时最安全。

📝 代码示例

<script setup>
import { ref } from 'vue'// 1. 基本类型
const count = ref(0) 
const message = ref('Hello Vue 3')// 2. 对象/数组 (虽然 reactive 也能做,但 ref 更通用)
const user = ref({ name: 'Alice', age: 25 })
const hobbies = ref(['Coding', 'Gaming'])// ⚠️ 注意:在 JS 逻辑中修改,必须加 .value
const increment = () => {count.value++ user.value.age = 26 // 修改对象属性hobbies.value.push('Reading')
}
</script><template><div><!-- 模板中自动解包,直接写 count --><p>计数: {{ count }}</p> <p>消息: {{ message }}</p><p>用户年龄: {{ user.age }}</p><button @click="increment">点我增加</button></div>
</template>

2. reactive:专为对象设计

核心定义:用于定义对象或数组类型的响应式数据。它基于 ES6 Proxy 实现。

💡 关键规则

  • JS 中访问不需要 .value,直接操作属性(例如 state.count)。
  • 限制
    1. 只能接收对象/数组,不能包裹基本类型。
    2. 解构会丢失响应性(除非配合 toRefs 使用,见下文避坑指南)。
    3. 不能直接替换整个对象(state = newObj 会失效),必须修改属性。

📝 代码示例

<script setup>
import { reactive } from 'vue'const state = reactive({count: 0,user: { name: 'Bob' }
})const update = () => {state.count++       // ✅ 正确:直接修改属性state.user.name = 'Tom' // ✅ 正确// state = { count: 1 } // ❌ 错误:这样会切断响应式连接!
}
</script>

⚖️ ref vs reactive 怎么选?

特性 ref reactive
数据类型 任意 (基本类型 + 对象) 仅限 对象/数组
访问方式 JS 中需 .value JS 中直接访问
替换值 count.value = 1 (✅ 支持) state = {} (❌ 不支持)
解构 天然支持 (const { c } = refObj) toRefs 辅助
2026 建议 🏆 推荐默认使用 ref 仅在确定不需要替换整个对象时使用

💡 专家建议:为了代码风格统一和避免“解构丢失响应性”的坑,现代 Vue 开发(尤其是配合 TypeScript 时)倾向于全部使用 ref


3. computed:带缓存的计算属性

核心定义:基于其他响应式数据计算得出的新数据。只有依赖变化时才会重新计算,否则直接返回缓存值。

💡 关键规则

  • 只读性:默认是只读的(Getter)。如果需要修改(Setter),需传入对象。
  • 自动追踪依赖:你在函数里用了哪些 refreactive,它自动就知道要监听谁。
  • 性能:比在模板里写复杂表达式或在 methods 里计算更高效。

📝 代码示例

<script setup>
import { ref, computed } from 'vue'const price = ref(100)
const quantity = ref(2)
const taxRate = ref(0.1)// 计算总价 (自动监听 price, quantity, taxRate)
const total = computed(() => {console.log('计算执行了...') // 只有依赖变了才会打印return price.value * quantity.value * (1 + taxRate.value)
})// 高级用法:可写的计算属性 (Getter + Setter)
const doublePrice = computed({get: () => price.value * 2,set: (val) => { price.value = val / 2 }
})
</script><template><p>单价: {{ price }}</p><p>数量: {{ quantity }}</p><p>总价 (含税): {{ total }}</p><button @click="doublePrice = 400">设置双倍价格为 400 (单价会变 200)</button>
</template>

4. watch & watchEffect:监听副作用

核心定义:当数据变化时,执行某些副作用(如:发送 API 请求、操作 DOM、打印日志)。

A. watch (精准监听)

  • 特点:懒执行(数据变了才跑),可以指定监听特定源,能获取新旧值。
  • 场景:路由变化、表单输入防抖、特定数据变更触发逻辑。
import { ref, watch } from 'vue'const searchQuery = ref('')// 监听单个 ref
watch(searchQuery, (newVal, oldVal) => {console.log(`搜索词从 "${oldVal}" 变成了 "${newVal}"`)// 这里可以发起 API 请求
})// 监听多个源
watch([searchQuery, someOtherRef], ([newQ, newOther], [oldQ, oldOther]) => {// ...
})

B. watchEffect (立即执行 + 自动收集依赖)

  • 特点立即执行一次,自动追踪函数内部用到的所有响应式数据。不用显式指定监听谁。
  • 场景:初始化加载数据、简单的日志记录。
import { ref, watchEffect } from 'vue'const userId = ref(1)// 只要 userId 变了,或者函数内用到的其他 ref 变了,都会重跑
watchEffect(() => {console.log(`当前用户 ID: ${userId.value}`)// fetchUser(userId.value) 
})
// ⚠️ 注意:上面代码组件挂载时会立即执行一次,无需手动调用

⚖️ watch vs watchEffect 怎么选?

特性 watch watchEffect
执行时机 懒执行 (数据变后才跑) 立即执行 (挂载时先跑一次)
依赖来源 显式指定 (第一个参数) 隐式自动收集 (函数里用了谁就监听谁)
获取旧值 ✅ 支持 (new, old) ❌ 不支持
推荐场景 需要旧值、防抖、特定字段变更 简单的副作用、初始化请求

🚫 Day 1 避坑指南 (必读!)

  1. reactive 解构陷阱

    const state = reactive({ count: 0 })
    let { count } = state // ❌ 错误!count 变成了一个普通数字,失去响应性// ✅ 正确做法 1 (推荐): 直接用 state.count
    // ✅ 正确做法 2: 使用 toRefs
    import { toRefs } from 'vue'
    const { count } = toRefs(state) // 现在 count 是一个 ref,有响应性
    

    这也是为什么建议大家多用 ref 的原因,ref 解构天然安全(在 <script setup> 中)。

  2. 忘记 .value
    <script> 里操作 ref 时,90% 的 bug 都是因为忘了写 .value

    count++ // ❌ 无效,只是修改了一个普通数字
    count.value++ // ✅ 正确
    
  3. computed 里做异步操作
    computed 必须同步返回一个值。如果你需要在数据变化时发请求,请用 watchwatchEffect


🎯 今日实战练习

请尝试在脑海中(或编辑器里)完成以下小任务:

  1. 创建一个 ref 名为 score,初始值为 0。
  2. 创建一个 computed 名为 level,逻辑是:score > 10 返回 "High",否则 "Low"。
  3. 创建一个 watch 监听 score,当分数变化时,在控制台打印 "分数更新了"。
  4. 创建一个按钮,点击让 score 加 5。

思考题:如果我把 score 改成 reactive({ val: 0 }),上面的代码需要怎么改?

http://www.jsqmd.com/news/428738/

相关文章:

  • 青少年近视防控为什么这么难?
  • 直接上结论:9个一键生成论文工具测评!研究生毕业论文+科研写作必备神器
  • 英文建站公司选择攻略:如何找到实力强、信誉好的专业服务商 - 品牌推荐大师
  • 让 Nginx for windows 服务开机自启
  • 2026烧烤加盟品牌优质推荐榜 高性价比低风险之选 - 优质品牌商家
  • 2026年自动锁螺丝机厂家推荐:深圳好伙伴智能科技专业供应吹气式/桌面式/多轴式全系产品 - 品牌推荐官
  • Windows纯本地部署OpenClaude:从零搭建你的7×24小时AI助理,打通微信/飞书
  • C++ 左值与右值(Lvalue/Rvalue)全解析
  • 腾讯应用宝创新服务模式,打造便捷高效移动应用新体验
  • 2026最新重庆会展策划公司权威推荐:3家实力企业助你高效落地 - 深度智识库
  • python+flask+vue框架的档案数字化项目沟通协作管理系统
  • 从“HALO交易”到电力系统智能化:霍尔电流传感器如何助力新能源革命?
  • 2026最新重庆活动策划公司排行榜:实力机构盘点与选择指南 - 深度智识库
  • 解锁语言潜能:2026年优选六大少儿英语培训机构推荐 - 品牌2026
  • python+flask+vue框架的电子政务服务预约管理系统
  • react基础讲义
  • 字典
  • 2026冷喂料橡胶挤出机品牌深度评测报告 - 优质品牌商家
  • 重庆校园文化建设公司推荐:2026年最新校方首选清单(附真实案例) - 深度智识库
  • 2026零基础烧烤加盟项目推荐榜低回本优选:特色烧烤加盟、知名烧烤品牌、自助烧烤加盟、适合小白的餐饮加盟选择指南 - 优质品牌商家
  • 2026汽车应急启动电源怎么选?全方位参数对比与选购建议 - 品牌2026
  • 元宇宙实验室哪家公司好?津发科技入选工信部元宇宙标准化工作组2025年度标准化工作先进集体、先进委员 - 品牌推荐大师1
  • 2026年全国有机肥厂家哪家口碑好?口碑出众且适配不同规模种植 - 深度智识库
  • 2026汽车电瓶设备公司推荐指南:如何挑选靠谱的汽车电瓶设备供应商? - 品牌2026
  • PHP的interface PaymentService {的庖丁解牛
  • 2026重庆展厅设计公司推荐:3家实力派清单,落地性拉满 - 深度智识库
  • 开源免费、部署简单的在线评测系统(OJ)
  • 锁相放大器SR830与OE1022性能对比分析
  • 零基础从零到一写一个 Hello World 级别的测试用例的庖丁解牛
  • nmn哪个牌子好高活性纯度2026年性价比高的十大nmn品牌榜推荐哪款? - 速递信息