人人都会AI编程

3.1 响应式数据基础:数据驱动视图的直观体验

更新时间:2026-07-11

在 1.2 节我们提过,Vue 的核心设计思想之一是“数据驱动视图”。这一节,我们不谈原理,直接通过最简短的代码来感受这种工作方式——你会发现,原来让页面动起来可以这么简单。

一个会自己更新的计数器

假设你正在写一个页面,上面显示一个数字,下面有个按钮,点一下数字就加 1。用原生 JavaScript 写,大概是这样的:

<span id="num">0</span>
<button id="btn">+1</button>

<script>
let count = 0;
const span = document.getElementById('num');
const btn = document.getElementById('btn');

btn.addEventListener('click', () => {
  count++;
  span.textContent = count; // 手动更新 DOM
});
</script>

你改完数据(count++)之后,必须再手动找到对应的 DOM 节点,把新值塞进去。数据是数据,视图是视图,中间需要你来当“接线员”。

现在换成 Vue,在单文件组件里写:

<template>
  <div>
    <span>{{ count }}</span>
    <button @click="count++">+1</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>

我们干了什么?

  • ref(0) 声明了一个响应式数据 count,初始值为 0
  • 在模板里用 {{ count }} 输出它的值。
  • 给按钮绑定了一个点击事件 @click="count++",直接修改 count

运行结果: 点击按钮,页面上的数字自动从 0 变成 1、2、3……整个过程,我们没有写任何 document.getElementById、没有操作 textContent,甚至没有“刷新页面”的动作。改了数据,界面就跟着变,一步到位。

这就是数据驱动视图最直观的体验:数据是大脑,视图是身体,它们天然同步。你只需要关心数据怎么变,视图更新交给 Vue。

另一个例子:让输入框“活”起来

再看一个更常见的场景:一个输入框,你希望输入什么,下面就实时显示什么。

原生 JS 需要监听 input 事件,然后手动改下面段落的内容。而 Vue 里,我们用一个叫 v-model 的指令就能“绑死”数据和输入框:

<template>
  <div>
    <input v-model="message" placeholder="随便打字" />
    <p>你输入的是:{{ message }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const message = ref('')
</script>

打开页面,在输入框里敲几个字,下面段落里的文字会同步变化,没有任何延迟感。背后的逻辑是:v-model 自动监听了输入框的变化,把新值写回 message;而 message 一变,所有用到它的模板位置({{ message }})自动更新。

这种“写了一条数据,全页面跟进”的能力,就是响应式系统的价值。它让界面开发从“命令式操控 DOM”变成了“声明式描述数据关系”,你不再需要:

  • 记着更新每一个关联位置
  • 担心不同地方的相同数据出现不一致
  • 写一堆事件监听和选择器

响应式数据的两种基本形态:ref 和 reactive

上面我们用到了 ref,它是 Vue 3 里定义基本类型(如数字、字符串、布尔值)响应式数据最常用的 API。它的语法是:

import { ref } from 'vue'
const count = ref(0)        // 初始值 0
const name = ref('Alice')   // 初始值 'Alice'

<script setup> 里,你可以直接通过 count.value 来读取或修改它的值。但在 <template> 里,Vue 会自动帮你“解包”,所以直接写 {{ count }} 就行,不用加 .value

当你要处理一个对象或数组(引用类型)时,通常用 reactive

import { reactive } from 'vue'
const user = reactive({
  name: 'Bob',
  age: 25
})

现在,user.nameuser.age 都是响应式的。你在模板里可以直接写 {{ user.name }};在脚本里修改 user.age = 26,界面同样自动更新。

选择小技巧:

  • 如果只是存一个单一值(一个数字、一个字符串),用 ref
  • 如果有一组关联的数据(比如表单字段、用户信息),放在 reactive 对象里更自然。

其实你可以完全只用 ref,因为 ref 也能包住对象。但 reactive 处理对象时“不需要 .value”,用起来更贴近直觉。随着经验上来,你会有自己的偏好。

真实开发中的体验

假设你要做一个简单的任务清单:

<template>
  <div>
    <input v-model="newTask" @keyup.enter="addTask" placeholder="添加任务" />
    <ul>
      <li v-for="task in tasks" :key="task.id">{{ task.text }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
const newTask = ref('')
const tasks = reactive([])

function addTask() {
  if (newTask.value.trim()) {
    tasks.push({
      id: Date.now(),
      text: newTask.value
    })
    newTask.value = '' // 清空输入框
  }
}
</script>

这个简单的 Todo 涵盖了几个关键点:

  • newTask 和输入框双向绑定,输入框内容变了,newTask 自动跟上。
  • 点击回车,往 tasks 数组里追加一条新任务——用的是 push,这和我们操作普通数组一模一样。
  • 界面通过 v-for 遍历 tasks,当数组新增元素时,列表自动多出一行。

整个过程你只需要关心“数组里有多少任务”“如何添加、删除任务”,视图自动根据数组内容重新渲染。你就是真正在写“业务逻辑”,而不是在写“DOM 操作”。

为什么这叫“直观”?

很多新手第一次运行上面的代码都会被这个流畅感所打动。不是因为技术有多高深,而是因为思维负担极低:你想让页面显示什么,就声明什么;想让它变,改数据就行。框架替你干了所有同步的脏活累活。

这种开发体验一旦习惯了,就再也回不去手动操作 DOM 的时代了。后面几章我们会详细拆解这套响应式系统到底是怎么运作的,但现在,你只需要知道:在 Vue 里,写界面 = 写数据 + 写模板,而你最该关注意的是数据。