Vue3 零基础每日笔记(005):ref 响应式基础——为什么 script 里要 .value

一、前言

前几篇代码里一直在写 ref,今天终于把它讲透。这是 Vue3 响应式的第一块基石:什么是响应式?为什么 script 里要 .value,模板里又不用?为什么普通变量页面不更新?一篇全部搞懂。

二、先理解:什么是响应式

写个反例感受一下,用普通变量渲染页面:

vue 复制代码
<template>
  <p>{{ msg }}</p>
  <button @click="msg = '我变了'">改数据</button>
</template>

<script setup>
// 普通变量,没有包 ref
let msg = '我一开始的样子'
</script>

点击按钮,页面纹丝不动。因为 Vue 根本不知道 msg 变了------它不是响应式数据。

换成 ref 包一层:

vue 复制代码
<template>
  <p>{{ msg }}</p>
  <button @click="msg = '我变了'">改数据</button>
</template>

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

// ref 包一层,变成响应式数据
const msg = ref('我一开始的样子')
</script>

再点,页面立刻更新 。这就是响应式:数据变化时,用到它的页面部分自动更新。

三、ref 的本质:给数据套一层"监控壳"

通俗理解:ref(初始值) 会把你的数据装进一个带监控的盒子,Vue 盯着这个盒子,数据一有风吹草动,立刻通知页面更新。

在 script 里打印看看 ref 到底长什么样:

js 复制代码
const count = ref(0)
console.log(count)
// 输出:RefImpl { _value: 0, ... }

它是个对象,真正的值藏在 .value 属性里。所以------

script 里操作数据,必须通过 .value:

js 复制代码
const count = ref(0)

count.value        // 读:拿到 0
count.value = 10   // 写:改成 10,页面自动更新
count.value += 5   // 自增:变成 15

console.log(count)      // ❌ 打印的是盒子对象,不是 0
console.log(count.value) // ✅ 打印 0

模板里不用 .value,直接写名字:

vue 复制代码
<template>
  <!-- ✅ 直接写 count,不用 count.value -->
  <p>{{ count }}</p>
</template>

为什么模板里不用?因为 Vue 编译模板时会自动帮你解包 (自动加 .value)。这不是语法不一致,是 Vue 设计好的------模板里写 .value 反而多余。

一句话记住:script 里要 .value,模板里不用。

四、ref 包对象:深层响应式

ref 不只能包数字字符串,包对象、数组也完全没问题,而且是深层响应式------嵌套多深的属性改了都会更新:

vue 复制代码
<template>
  <p>姓名:{{ user.name }}</p>
  <p>城市:{{ user.address.city }}</p>
  <button @click="user.address.city = '上海'">搬家</button>
</template>

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

const user = ref({
  name: '小明',
  address: {
    city: '北京'  // 深层嵌套,依然响应式
  }
})
</script>

点按钮,"城市:上海"立刻更新。深层属性通过 .value 一路点下去访问,模板里 Vue 自动解包,直接 user.address.city 就行。

五、三个高频新手坑(重点看)

坑 1:忘了 .value,页面不更新

js 复制代码
const count = ref(0)

function add() {
  count++           // ❌ 改的是盒子上的临时值,页面不动
  count.value++     // ✅ 正确
}

现象:代码不报错(或报 NaN),但页面死活不更新。90% 是忘写 .value。

坑 2:解构后丢失响应式

js 复制代码
const count = ref(0)

// ❌ 解构的一瞬间,就把"当时的值 0"复制出来了
// 之后 count.value 再怎么变,num 都是死的 0
let num = count.value

// ✅ 要用就用 count.value,别提前解构

(解构对象的场景要用 toRefs,那是 012 篇的主角,这里先埋个伏笔。)

坑 3:异步/延时后页面不更新?不存在的

js 复制代码
const msg = ref('加载中')

// 定时器里改 ref,页面照样更新,放心用
setTimeout(() => {
  msg.value = '加载完成'
}, 1000)

有些框架的数据脱离响应式要在特定时机刷新,Vue3 的 ref 在任何地方改都触发更新------这正是它的强大之处。

六、综合实战:点赞 + 自动切换文案

用 ref 写个完整交互小案例,把本篇知识点全用上:

vue 复制代码
<template>
  <h3>{{ article.title }}</h3>
  <p>{{ liked ? '❤️ 已点赞' : '🤍 点个赞吧' }}</p>
  <p>点赞数:{{ count }}</p>
  <button @click="toggle">{{ liked ? '取消赞' : '点赞' }}</button>
</template>

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

const article = ref({ title: 'Vue3 ref 一篇讲透' })
const count = ref(66)
const liked = ref(false)

function toggle() {
  if (liked.value) {
    liked.value = false
    count.value--        // 取消赞,数量减 1
  } else {
    liked.value = true
    count.value++        // 点赞,数量加 1
  }
}
</script>

运行后:点赞按钮在两种文案间切换,数字实时增减,emoji 同步变化------三个 ref 数据联动,页面全自动更新。

【截图位置:点赞前(灰心+66)与点赞后(红心+67)的对比效果】

七、今日小结

  • 响应式 = 数据变,视图自动变
  • ref(初始值) 创建响应式数据,真值存在 .value 里
  • script 里 .value,模板里不用
  • ref 包对象是深层响应式
  • 三大坑:忘 .value、提前解构、(误以为)异步不更新

作者:陈志宝

下篇预告

ref 什么都能包,但每次都 .value 有点烦?下一篇讲它的好兄弟 reactive:专门包对象、不用 .value,以及两者到底该选谁。

相关推荐
福兮说3 小时前
前端下载大文件,点了按钮半天没反应:fetch + blob 的三个问题和三种替代写法
前端·javascript·性能优化·文件下载
GreenTea3 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
科技观察哨4 小时前
六足平台选型与纳米定位系统集成:HEB-640六自由度位移台在半导体光刻对准中的参数边界与国产替代评估
前端·人工智能
niucloud-admin4 小时前
JAVA V6 多商户商城 开发文档——web前端
前端
颜进强4 小时前
16 · NestJS LifecycleEvents 生命周期事件:五个钩子、三条边界,和 rag-server 的优雅停机
前端·后端·ai编程
今年下半年6 小时前
【VUE】整合腾讯地图、自定义区域边界、村委名称及资产统计(放大显示资产点位)
前端·javascript·vue.js
郑州光合科技余经理6 小时前
同城电商系统:库存变更怎么同步到订单
java·开发语言·前端·后端·uni-app·php·ai编程
L@ncor7 小时前
第五章 基于低代码平台的智能体搭建 · 学习笔记(Coze / Dify / FastGPT / n8n)
笔记·学习·低代码·agent·prompt工程
知潮网7 小时前
被嫌弃的五仁月饼,一吨千元在养猪场翻了身
笔记
2601_960631237 小时前
中药前处理及提取的工艺环节与技术要点
笔记