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

Vue3 响应式系统:`ref`/`reactive`/`watchEffect` 的工作方式与最佳实践

你写 Vue3 时经常会遇到这些“似懂非懂”的问题:

  • 为什么ref需要.value
  • 为什么reactive解构后就“不响应”了?
  • watchwatchEffectcomputed到底怎么选?

这篇不从源码堆概念,而是用“你在项目里一定会踩的坑”为主线,把 Vue3 响应式讲清楚。


1. 响应式的目标是什么

目标只有一个:

  • 当某个状态发生变化时,能定位到“谁用到了它”,并触发对应的更新。

对应两个关键动作:

  • 依赖收集:谁在读取这个状态?
  • 派发更新:这个状态变了,要通知谁重新执行?

Vue3 用 Proxy/Reflect + effect(副作用函数)完成这件事。


2.reactive:对象的响应式

reactive(obj)返回一个 Proxy:

  • 读取属性(get)时:收集依赖
  • 修改属性(set)时:触发更新

适合:

  • 状态本身是一个对象/数组
  • 需要深层属性自然响应

2.1 常见坑:解构丢响应

conststate=reactive({count:0})const{count}=state count++// 这里不会触发视图更新

原因:解构得到的是一个普通值,不再经过 Proxy 的get

解决:

  • toRefs(state)/toRef(state, 'count')
import{reactive,toRefs}from'vue'conststate=reactive({count:0})const{count}=toRefs(state)count.value++

3.ref:单值/原始类型的响应式

ref(0)返回形如{ value: 0 }的对象,它本身也会被响应式处理。

为什么要有.value

  • 为了让原始类型也能用同一套“依赖收集/触发更新”的机制

适合:

  • 数字/字符串/布尔值等原始类型
  • 你希望“一个变量就是一个状态”

3.1ref包对象会怎样

constuser=ref({name:'a'})user.value.name='b'// 也能响应

本质上:ref内部会把对象也做成响应式(类似reactive)。


4.computed:带缓存的派生状态

特点:

  • 懒执行:没人读就不算
  • 有缓存:依赖不变不重新算

适合:

  • 由多个状态推导出的展示字段
  • 避免把“计算逻辑”散落在模板里

5.watchvswatchEffect

5.1watch

  • 你明确知道要监听谁(source)
  • 你需要 old/new
  • 你需要精细控制(immediate / deep / flush)
watch(()=>state.keyword,(newVal,oldVal)=>{fetchList(newVal)},{immediate:true})

5.2watchEffect

  • 你不想声明 source
  • 只要 effect 内读取到的响应式数据变化,就重新执行
watchEffect(()=>{fetchList(state.keyword)})

注意:watchEffect很方便,但也更容易“无意间读到多余依赖”导致频繁触发。


6. 实战建议(项目里最实用的选择策略)

  • 状态是对象/表单:优先reactive,配合toRefs暴露字段
  • 状态是单值:优先ref
  • 派生展示字段:用computed
  • 请求联动:优先watch(能控粒度、能拿 old/new、好排查)
  • 自动收集依赖的副作用:再用watchEffect

7. 常见坑与排查

  • 响应式失效:优先检查“是否解构了reactive
  • watch 不触发:检查 source 是否是 getter(watch(() => obj.x, ...)
  • 频繁触发:检查watchEffect内是否读取了多余状态

8. 总结

  • Vue3 响应式核心是:依赖收集 + 派发更新
  • reactive适合对象,但解构会丢响应,需要toRefs
  • ref让原始类型也能响应式
  • computed做派生字段,带缓存
  • watch更可控,watchEffect更省事但更容易过度依赖
http://www.cnnetsun.cn/news/1422473.html

相关文章:

  • Deepin系统防火墙配置全攻略:从UFW安装到端口开放(附Windows测试方法)
  • Linux系统管理员必看:logrotate权限问题终极解决方案(附su指令详解)
  • Superset零基础安装指南:从Python环境配置到可视化仪表盘搭建(避坑大全)
  • 从用户行为分析到金融报表:手把手教你用Apache Doris的BITMAP和HLL搞定大数据去重
  • FPGA实战:如何在Vivado中快速配置HDMI 1.4/2.0 TX Subsystem IP(附时钟域避坑指南)
  • 别再背公式了!用“找人帮忙“彻底搞懂 Self-Attention
  • ESP32实现iOS ANCS通知通信的嵌入式开发指南
  • DevOps03-GitLab02-持续集成与部署(CI/CD)01:简介(最简洁版Pipeline:编写.gitlab-ci.yml文件)【GitLab CI/CD 对标 Jenkins】
  • 计算机毕设 java基于java的小区物业管理系统 Java 小区物业综合管理平台的设计与实现 基于 SpringBoot 的智能化小区物业管理系统开发
  • Spring Boot中RedisTemplate和StringRedisTemplate混用的那些坑(附解决方案)
  • 视频监控音频协议选型指南:AAC、G711A、G711U如何选?附实战案例
  • Windows下PCL点云库多版本共存实战:VS2019到VS2022无缝切换指南
  • 以根深铸远势——AI元人文视角下的中国神话跨文化传播 副篇:伦理中间件中的交往理性——从尊重自感开始
  • 如何用SpaceNet SN6数据集进行多传感器建筑识别(附Rotterdam数据下载指南)
  • Python实战:高效批量转换手机通讯录为vcf格式的解决方案
  • PostgreSQL字符串截取实战:从基础到正则表达式的高级用法
  • GetOrganelle实战指南:从安装到高效组装叶绿体基因组
  • 【技术揭秘】快速识别网站服务器类型:Nginx与Apache的实战技巧
  • uniapp实战:混合使用组件与API,优雅实现图片与视频的上传与预览
  • 当四足机器狗遇上3D激光雷达:为何放弃Gmapping,选择Hector SLAM构建栅格地图?
  • 真心不骗你!碾压级的降AI率网站 —— 千笔·降AIGC助手
  • VS2010+OpenCV2.4.9环境下的Zbar二维码识别实战(附完整代码)
  • SpringBoot3与OAuth2.1深度整合:从/oauth/token到/oauth2/token的平滑迁移指南
  • 告别官方限制!这款Github 52.7K Stars的ChatGPT桌面客户端,老Mac/Win/Linux都能用
  • sdut-python-实验六-面向对象编程
  • Hutool之Http工具类URL编码问题解析
  • 从ImageNet到RingMo:为什么遥感领域需要专属基础模型?
  • 救命神器!全行业通用AI论文网站,千笔ai写作 VS 学术猹
  • OpenClaw定时任务实践:GLM-4.7-Flash实现24/7自动化监控
  • 如何用毫米波雷达实现8.6米非接触式生命体征监测?mmVital-Signs完整指南