Vue3 响应式原理与 Composition API 实战踩坑:我被这些细节坑了3次后终于搞懂了
Vue3 响应式原理与 Composition API 实战踩坑我被这些细节坑了3次后终于搞懂了前言我的踩坑血泪史大家好我是二白一个刚入门 Vue3 不久的后端开发。之前一直用 Vue2觉得响应式用起来很简单直到项目升级到 Vue3 后我被 Composition API 和响应式系统坑了无数次。印象最深的是那次做一个后台管理系统页面数据怎么都不更新我改来改去改了3个小时最后才发现是ref和reactive用混了。还有一次用watch监听一个对象改动属性后监听器怎么都不触发浪费了一下午排查。今天把这些踩坑经验整理出来希望能帮到和我一样刚入坑 Vue3 的同学。文章会从最基础的响应式原理讲起然后分享 5 个我在项目中真实遇到的坑每个坑都会给出完整代码示例。一、ref vs reactive到底该用哪个问题描述刚开始用 Vue3 时我完全搞不清楚什么时候用ref什么时候用reactive。为了方便我总是随手用其中一个结果经常遇到数据不更新或者控制台报警告的问题。有一次我这样写import{ref,reactive}fromvue// 随手用 refconstformref({name:,email:})// 修改数据functionupdateName(){form.name张三// 报错了form.value 才是实际值}原因分析ref和reactive的核心区别在于ref接收原始值返回一个响应式对象。访问和修改值需要通过.valuereactive接收对象返回一个响应式代理直接访问属性即可Vue2 升级到 Vue3 后为了解决响应式丢失问题Vue3 使用了 Proxy但ref是创建一个通过包含.value属性的对象来实现响应式的。解决方案记住一个简单规则基本类型用 ref对象/数组用 reactiveimport{ref,reactive}fromvue// 基本类型用 refconstcountref(0)constmessageref(Hello)// 对象/数组用 reactiveconstuserreactive({name:张三,age:25})constlistreactive([])// 修改方式functionupdate(){count.value// ref 需要 .valueuser.name李四// reactive 直接修改}完整示例template div p计数{{ count }}/p p用户{{ user.name }} - {{ user.age }}/p button clickincrement1/button button clickchangeUser换人/button /div /template script setup import { ref, reactive } from vue // 基本类型用 ref const count ref(0) // 对象用 reactive const user reactive({ name: 张三, age: 25 }) function increment() { count.value } function changeUser() { user.name 李四 user.age 30 } /script二、reactive 对象重新赋值失效问题描述这个问题坑了我很久。我用reactive定义了一个对象后来想整个替换它结果发现数据完全不更新。conststatereactive({list:[]})// 模拟接口返回新数据functionloadData(){state.list[1,2,3]// 不生效}原因分析reactive返回的是一个 Proxy 对象它会追踪属性的读取和修改。但是如果你把整个对象重新赋值响应式就会断开。因为state.list [1, 2, 3]是给state这个 Proxy 的list属性重新赋值而不是修改原有数组Vue3 可以检测到。但如果是这样conststatereactive({list:[]})// 错误整个替换 reactive 对象statereactive({list:[1,2,3]})这完全没有响应性因为state本身被重新赋值了。解决方案如果你需要重置整个对象有几种方式方式1使用 ref 包装对象import{ref}fromvueconststateref({list:[]})functionloadData(){state.value{list:[1,2,3]}}方式2使用 Object.assignconststatereactive({list:[]})functionloadData(){Object.assign(state,{list:[1,2,3]})}方式3清空后重新赋值conststatereactive({list:[]})functionloadData(){state.list.length0// 清空state.list.push(1,2,3)// 重新添加}完整示例template div ul li v-foritem in state.list :keyitem{{ item }}/li /ul button clickloadData加载数据/button button clickreset重置/button /div /template script setup import { reactive, ref } from vue // 推荐用 ref 包装整个对象 const state ref({ list: [], loading: false }) function loadData() { state.value.loading true // 模拟接口 setTimeout(() { state.value { list: [1, 2, 3, 4, 5], loading: false } }, 1000) } function reset() { state.value { list: [], loading: false } } /script三、watch 监听不到对象属性变化问题描述这是我踩过最坑的一个问题。我用watch监听一个 reactive 对象的一个属性结果修改属性后监听器完全不触发。constuserreactive({name:张三,info:{age:25}})watch(user.info,(newVal,oldVal){console.log(info 变化了,newVal)},{deep:true})// 修改了但监听器没触发user.info.age30原因分析问题在于user.info在监听时获取到的是一个普通对象而不是响应式对象。Vue3 的watch第一个参数需要是一个 ref一个响应式对象或者一个 getter 函数直接传入user.info会导致监听失效。解决方案使用 getter 函数或者监听整个对象import{reactive,watch}fromvueconstuserreactive({name:张三,info:{age:25}})// 方式1使用 getter 函数watch(()user.info,(newVal,oldVal){console.log(info 变化了,newVal)},{deep:true})// 方式2监听整个对象watch(user,(newVal,oldVal){console.log(user 变化了,newVal)},{deep:true})// 方式3监听单个属性watch(()user.info.age,(newVal,oldVal){console.log(age 变化了,newVal)})完整示例template div p姓名{{ user.name }}/p p年龄{{ user.info.age }}/p button clickuser.info.age年龄1/button /div /template script setup import { reactive, watch } from vue const user reactive({ name: 张三, info: { age: 25 } }) // 监听深层属性 - 使用 getter watch(() user.info.age, (newVal, oldVal) { console.log(年龄从 ${oldVal} 变为 ${newVal}) }) // 监听整个 info 对象 - 使用 getter deep watch(() user.info, (newVal, oldVal) { console.log(info 对象变化了, newVal) }, { deep: true }) // 监听整个 user watch(user, (newVal, oldVal) { console.log(user 变化了, newVal) }, { deep: true }) /script四、computed 计算结果不更新问题描述有时候我定义了 computed但是它就是不更新特别奇怪。constcountref(0)constmultiplierref(2)consttotalcomputed((){returncount.value*multiplier.value})functionincrement(){count.valueconsole.log(total:,total.value)// 居然没变化}原因分析这个问题通常是两个原因computed 依赖的值不是响应式的如果依赖的值不是 ref/reactivecomputed 就不会更新在 setup 中直接访问 computed 值computed 返回的也是一个 ref访问需要.value但在模板中不需要解决方案确保依赖是响应式的并正确访问 computed 值import{ref,computed}fromvueconstcountref(0)constmultiplierref(2)// computed 返回的也是 refconsttotalcomputed((){console.log(计算了)returncount.value*multiplier.value})functionincrement(){count.value// 访问 computed 值需要 .valueconsole.log(total:,total.value)}// 在模板中不需要 .value// {{ total }}完整示例template div p数量{{ count }}/p p单价{{ price }}/p p总价{{ total }}/p !-- 模板中直接用不需要 .value -- p折扣价{{ discountedPrice }}/p button clickcount增加数量/button /div /template script setup import { ref, computed } from vue const count ref(0) const price ref(100) const discount ref(0.8) // 计算属性 - 依赖响应式数据 const total computed(() { return count.value * price.value }) // 带缓存的计算属性 const discountedPrice computed(() { return total.value * discount.value }) // 也可以使用 getter/setter const doubleCount computed({ get: () count.value * 2, set: (val) { count.value val / 2 } }) function doubleIt() { doubleCount.value 100 // 会触发 set更新 count } /script五、onMounted 中拿不到最新的响应式数据问题描述这个问题很隐蔽。我在onMounted里面调用了一个 reactive 对象的数据结果发现数据是初始值不是最新的。constuserreactive({name:张三})onMounted((){console.log(user:,user.name)// 永远是张三})functionupdateName(){user.name李四}原因分析其实这个问题不是onMounted的问题而是异步回调的问题。如果你是在onMounted的异步回调中访问响应式数据要小心闭包陷阱。constuserreactive({name:张三})onMounted((){// 这个回调会在组件挂载后执行// 但 user 是响应式的这里应该能拿到最新值console.log(user:,user.name)})// 真正的坑异步回调onMounted((){setTimeout((){// 这里能拿到最新值吗能console.log(3秒后:,user.name)},3000)})实际上上面的代码应该能正常工作。真正的问题是这样的// 错误示例在 onMounted 中创建闭包onMounted((){constnameuser.name// 这里捕获了初始值// 3秒后打印的还是初始值setTimeout((){console.log(name)// 张三不是最新的},3000)})解决方案不要在onMounted中提前捕获响应式数据要在回调中直接访问import{reactive,onMounted}fromvueconstuserreactive({name:张三})// 正确直接在回调中访问onMounted((){console.log(挂载时:,user.name)setTimeout((){console.log(3秒后:,user.name)// 能拿到最新值},3000)})// 正确使用 watchimport{watch}fromvuewatch(()user.name,(newVal){console.log(name 变化了:,newVal)})functionupdateName(){user.name李四}完整示例template div p用户名{{ user.name }}/p button clickuser.name 李四改名/button button clickfetchUser异步获取用户/button /div /template script setup import { reactive, onMounted, watch } from vue const user reactive({ name: 张三, loading: false }) // 正确在 onMounted 中直接访问响应式对象 onMounted(() { console.log(挂载时:, user.name) // 异步回调中直接访问可以拿到最新值 setTimeout(() { console.log(异步回调:, user.name) }, 2000) }) // 使用 watch 监听变化 watch(() user.name, (newVal, oldVal) { console.log(name 从 ${oldVal} 变为 ${newVal}) }) // 模拟异步操作 function fetchUser() { user.loading true setTimeout(() { user.name 从接口获取的数据 user.loading false }, 1000) } /script总结Vue3 的 Composition API 确实比 Vue2 的 Options API 更灵活但也多了很多需要注意的细节。回顾一下这篇文章提到的 5 个坑ref vs reactive基本类型用 ref对象用 reactivereactive 重新赋值用 ref 包装对象或使用 Object.assignwatch 监听使用 getter 函数监听深层属性computed 访问记得 computed 也是 ref模板外需要.valueonMounted 闭包不要提前捕获值要在回调中直接访问响应式对象希望这些经验能帮你在 Vue3 的学习中少走弯路。如果觉得有用点个赞再走有问题也欢迎在评论区讨论我是二白一个正在成长的全栈开发者。关注我一起学习更多前端/vue3 开发经验

相关新闻

Flutter 三方库 meedu 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、基于反应式编程(Reactive)的工业级状态管理、依赖注入与全局响应式架构引擎

Flutter 三方库 meedu 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、基于反应式编程(Reactive)的工业级状态管理、依赖注入与全局响应式架构引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 meedu 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、基于反应式编程(Reactive)的工业级状态管理、依赖注入与全局响应式架构引擎 在鸿蒙&#x…

2026/5/17 8:21:54 阅读更多 →
基于NARX和NARX-LSTM的模型辨识与MPC控制系统调试与优化

基于NARX和NARX-LSTM的模型辨识与MPC控制系统调试与优化

基于NARX和NARX-LSTM的模型辨识与MPC控制系统调试与优化 1. 引言 在工业过程控制、金融预测、能源管理等领域,非线性系统建模与模型预测控制(MPC)是核心的技术手段。本项目旨在构建一个针对两输入单输出(MISO)非线性系统的辨识与控制框架。用户已经初步完成了三个程序:…

2026/5/17 8:21:54 阅读更多 →
市政雨污排口流量监测设备技术与应用

市政雨污排口流量监测设备技术与应用

一.引文市政雨污排口流量监测设备承担城市排水数据采集核心任务,这类设备以非接触与接触式测量相结合的技术路线,为雨污分流、污染溯源、防汛调度与水环境治理提供连续可靠的量化依据。工程人员把现场水流状态转化为可传输、可分析的数字信号…

2026/6/20 4:26:14 阅读更多 →

最新新闻

真人克隆口播小程序开发全攻略:AI数字人系统源码架构解析

真人克隆口播小程序开发全攻略:AI数字人系统源码架构解析

随着生成式AI不断发展,"真人克隆口播"正在成为短视频、自媒体、电商、知识付费等行业的新生产力。过去,一条视频需要真人出镜、反复拍摄、后期剪辑,如今借助AI数字人技术,只需录制少量素材,即可快速生成高度…

2026/7/5 6:31:52 阅读更多 →
抖音内容高效采集工具:如何用开源方案解决批量下载与管理的技术挑战

抖音内容高效采集工具:如何用开源方案解决批量下载与管理的技术挑战

抖音内容高效采集工具:如何用开源方案解决批量下载与管理的技术挑战 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser f…

2026/7/5 6:29:52 阅读更多 →
JMeter-Bzm-Plugins进阶指南:从安装部署到性能调优实战

JMeter-Bzm-Plugins进阶指南:从安装部署到性能调优实战

1. 项目概述:为什么Bzm-Plugins是JMeter进阶的必经之路如果你已经用了一段时间的JMeter,从录制几个简单的HTTP请求,到学会使用CSV参数化、正则表达式提取器,再到搭建分布式压测环境,你可能会觉得这个工具已经玩得差不多…

2026/7/5 6:27:51 阅读更多 →
包装线跨品牌通讯:EtherCAT 转 ProfiNet 网关实现 NJ501 读取 1734-AENT 计数与温度

包装线跨品牌通讯:EtherCAT 转 ProfiNet 网关实现 NJ501 读取 1734-AENT 计数与温度

一、项目背景与挑战某食品包装企业新建一条高速枕式包装生产线,用于糕点、面包等食品的自动化包装,产线要求稳定运行、数据实时采集、包装精度与效率同步提升。该生产线采用欧姆龙NJ501型EtherCAT主站PLC作为核心控制器,负责协调包装机、输送…

2026/7/5 6:25:51 阅读更多 →
本地AI智能体组合:Hermes与Codex打造自动化“赛博牛马”

本地AI智能体组合:Hermes与Codex打造自动化“赛博牛马”

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 这次我们来看一个关于 Hermes 和 Codex 的本地 AI 智能体组合方案。这个组合的核心目标,是打造一个能够长时间、自动化处理…

2026/7/5 6:19:50 阅读更多 →
FreeCAD源码分析: Selection Model

FreeCAD源码分析: Selection Model

本文从业务分析与逻辑推理出发,旨在研究FreeCAD中Selection Model的相关实现原理。 注1:限于研究水平,分析难免不当,欢迎批评指正。 注2:文章内容会不定期更新。 一、概述 在图形交互系统中,“选择”通常是用户意图进入系统内部处理链路的第一个明确动作。对于 FreeCA…

2026/7/5 6:17:50 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻