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,以及两者到底该选谁。

相关推荐
茶底世界之下1 小时前
林间步道要不要加暗角?用 Awaking 把强度收在20%
前端·ios·图片资源
执子念的飞鱼1 小时前
纯前端预览 275 种文件格式是怎么做到的
前端·vue.js·react.js
xier_ran1 小时前
【infra之路】AI 编译器学习笔记(十四):为什么要 tiling,以及矩阵计算单元的“固定“
人工智能·笔记·学习
行者全栈架构师2 小时前
【鸿蒙心迹】互动卡片全链路——把核心功能放到桌面(FormKit 实战)(HarmonyOS 7.x)
前端·算法·架构
java1234_小锋2 小时前
【技术专题】HTML5 网页技术 - HTML5 表单基础以及增强
前端·html·html5
燕落南枝2 小时前
HTML 学习:辅助标签 label 与字符实体
前端·学习·html
不简说2 小时前
AI 帮你画模板、点一下就云打印:sv-print 这三个月都干了啥
前端·javascript·人工智能
Amos_Web2 小时前
Rspack 源码解析(二十六):CssPlugin 与 CssChunkingPlugin 如何保证 CSS 顺序并切分 chunk
前端
孙浩君2 小时前
0行手写代码,我用AI做了一个打工人模拟器(含3D霓虹城市漫游)
前端·后端
夏天要喝冰可乐2 小时前
用 Codex 连接 Blender,我做了一块可以走进楼宇的大屏
前端·three.js