跳到主要内容

第 3 节:响应式数据与双向绑定

📚 本节目标

  • 理解什么是"响应式"
  • 掌握 v-model 指令的基本用法
  • 学会在输入框、复选框、单选框中使用 v-model
  • 理解 v-bind 和 v-model 的区别

一、什么是"响应式"?

传统 JavaScript 的方式

<input type="text" id="nameInput">
<p id="display"></p>

<script>
let name = '';

// 需要手动监听输入
document.getElementById('nameInput').addEventListener('input', function(e) {
name = e.target.value;
// 需要手动更新页面
document.getElementById('display').innerText = '你好,' + name;
});
</script>

问题:要写很多代码,手动监听、手动更新。

Vue 的响应式方式

<input type="text" v-model="name">
<p>你好,{{ name }}</p>

一行代码搞定! Vue 自动帮你:

  • 监听输入框的变化
  • 更新 name 数据
  • 更新页面显示

大白话解释"响应式"

  • 数据变了,页面自动跟着变
  • 页面变了(用户输入),数据也自动跟着变
  • 这种"双向自动同步"就叫响应式

二、v-model 指令详解

基本用法:文本输入框

完整示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-model 基础示例</title>
<style>
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
border: 2px solid #42b983;
border-radius: 10px;
}
input {
padding: 10px;
font-size: 16px;
width: 100%;
margin: 10px 0;
}
.result {
background: #f0f0f0;
padding: 15px;
border-radius: 5px;
margin-top: 20px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>自我介绍表单</h2>

<!-- 输入框与 name 数据双向绑定 -->
<label>你的名字:</label>
<input type="text" v-model="name" placeholder="请输入你的名字">

<label>你的年龄:</label>
<input type="number" v-model="age" placeholder="请输入你的年龄">

<!-- 实时显示数据 -->
<div class="result">
<h3>实时预览:</h3>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p v-if="name">你好,{{ name }}!欢迎来到 Vue 的世界!</p>
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
name: '', // 初始值为空字符串
age: '' // 初始值为空字符串
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  1. 在输入框中输入文字
  2. 下方的"实时预览"区域会立即更新
  3. 不需要点击任何按钮,完全自动!

v-model 与复选框

单个复选框

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>复选框示例</title>
<style>
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
.checkbox-item {
margin: 15px 0;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>用户协议</h2>

<div class="checkbox-item">
<!-- 绑定布尔值:勾选为 true,不勾选为 false -->
<label>
<input type="checkbox" v-model="agreed">
我已阅读并同意用户协议
</label>
</div>

<p>协议状态:{{ agreed ? '已同意' : '未同意' }}</p>

<!-- 根据是否勾选来禁用/启用按钮 -->
<button :disabled="!agreed">注册</button>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
agreed: false // 默认未勾选
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 初始状态:按钮是灰色的(禁用状态)
  • 勾选复选框后:按钮变成可点击
  • agreed 的值会自动变成 truefalse

多个复选框

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>多复选框示例</title>
</head>
<body>
<div id="app">
<div class="container">
<h2>选择你喜欢的水果</h2>

<!-- 多个复选框绑定到同一个数组 -->
<label><input type="checkbox" v-model="fruits" value="苹果"> 苹果</label><br>
<label><input type="checkbox" v-model="fruits" value="香蕉"> 香蕉</label><br>
<label><input type="checkbox" v-model="fruits" value="橙子"> 橙子</label><br>
<label><input type="checkbox" v-model="fruits" value="葡萄"> 葡萄</label><br>

<p>你选择了:{{ fruits }}</p>
<p>共选择了 {{ fruits.length }} 种水果</p>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
fruits: [] // 空数组,勾选的值会自动添加进来
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 勾选"苹果":fruits 变成 ['苹果']
  • 再勾选"香蕉":fruits 变成 ['苹果', '香蕉']
  • 取消勾选"苹果":fruits 变成 ['香蕉']

v-model 与单选框

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>单选框示例</title>
</head>
<body>
<div id="app">
<div class="container">
<h2>选择你的性别</h2>

<!-- 多个单选框绑定到同一个数据 -->
<label><input type="radio" v-model="gender" value=""></label>
<label><input type="radio" v-model="gender" value=""></label>

<p>你选择的性别是:{{ gender }}</p>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
gender: '' // 初始值为空
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 点击"男":gender 变成 '男'
  • 点击"女":gender 变成 '女'
  • 单选框一次只能选一个

三、v-bind 和 v-model 的区别(重要)

这是新手最容易混淆的地方!

对比表格

特性v-bind(:)v-model
数据流向单向:数据 → 页面双向:数据 ⇄ 页面
用途绑定属性(src、href 等)绑定表单输入
数据变化页面会变,但用户操作不会改变数据用户输入会自动改变数据
典型场景显示图片、链接、样式输入框、复选框、单选框

通俗比喻

v-bind:单向传话筒

  • 你(数据)说话,观众(页面)听到
  • 观众说话(用户操作),你听不到

v-model:双向对讲机

  • 你说话,对方听到
  • 对方说话,你也听到

代码对比

<!-- v-bind:只能从数据到页面 -->
<input :value="message"> <!-- 显示 message 的值,但输入不会改变 message -->

<!-- v-model:数据和页面双向同步 -->
<input v-model="message"> <!-- 显示 message 的值,输入会改变 message -->

试一试

<div id="app">
<p>数据值:{{ message }}</p>

<!-- 用 v-bind,输入不会改变 message -->
<input :value="message" placeholder="用 v-bind">

<!-- 用 v-model,输入会改变 message -->
<input v-model="message" placeholder="用 v-model">
</div>

在第二个输入框输入,第一个输入框和文字都会更新!


四、综合练习:简单的问卷调查

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>问卷调查</title>
<style>
.survey {
max-width: 600px;
margin: 50px auto;
padding: 30px;
border: 2px solid #42b983;
border-radius: 10px;
background: #f9f9f9;
}
.question {
margin-bottom: 25px;
padding: 15px;
background: white;
border-radius: 5px;
}
.result {
background: #e7f7ed;
padding: 20px;
border-radius: 5px;
margin-top: 30px;
}
input, textarea {
margin: 5px 0;
}
textarea {
width: 100%;
padding: 10px;
}
</style>
</head>
<body>
<div id="app">
<div class="survey">
<h2>📝 Vue 学习问卷</h2>

<!-- 问题1:文本输入 -->
<div class="question">
<label><strong>1. 你的名字:</strong></label><br>
<input type="text" v-model="name" placeholder="请输入姓名">
</div>

<!-- 问题2:单选框 -->
<div class="question">
<strong>2. 你的编程经验:</strong><br>
<label><input type="radio" v-model="experience" value="新手"> 新手</label><br>
<label><input type="radio" v-model="experience" value="有一点基础"> 有一点基础</label><br>
<label><input type="radio" v-model="experience" value="比较熟练"> 比较熟练</label>
</div>

<!-- 问题3:多选框 -->
<div class="question">
<strong>3. 你想学习哪些内容?(可多选)</strong><br>
<label><input type="checkbox" v-model="interests" value="组件"> 组件开发</label><br>
<label><input type="checkbox" v-model="interests" value="路由"> Vue Router</label><br>
<label><input type="checkbox" v-model="interests" value="状态管理"> Pinia 状态管理</label><br>
<label><input type="checkbox" v-model="interests" value="项目实战"> 项目实战</label>
</div>

<!-- 问题4:多行文本 -->
<div class="question">
<strong>4. 对课程的建议:</strong><br>
<textarea v-model="suggestion" rows="4" placeholder="写下你的建议..."></textarea>
</div>

<!-- 问题5:复选框(同意) -->
<div class="question">
<label>
<input type="checkbox" v-model="agreed">
我同意提交这份问卷
</label>
</div>

<!-- 提交按钮 -->
<button :disabled="!agreed" @click="submit">提交问卷</button>

<!-- 实时预览 -->
<div class="result" v-if="name">
<h3>📊 你的回答预览:</h3>
<p><strong>姓名:</strong>{{ name }}</p>
<p><strong>编程经验:</strong>{{ experience }}</p>
<p><strong>学习兴趣:</strong>{{ interests.join('、') || '未选择' }}</p>
<p><strong>建议:</strong>{{ suggestion || '无' }}</p>
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
name: '',
experience: '',
interests: [],
suggestion: '',
agreed: false
}
},
methods: {
submit() {
alert('问卷提交成功!感谢 ' + this.name + ' 的参与!');
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 填写表单时,下方实时显示你的回答
  • 必须勾选"同意"才能提交
  • 点击提交后弹出提示

五、本节重点回顾

响应式 = 数据变页面变,页面变数据也变
v-model = 双向数据绑定,用于表单元素
v-bind = 单向数据绑定,用于标签属性

v-model 用法总结

表单元素绑定的数据类型示例
文本输入框字符串<input v-model="name">
数字输入框字符串/数字<input type="number" v-model="age">
单个复选框布尔值<input type="checkbox" v-model="agreed">
多个复选框数组<input type="checkbox" v-model="arr" value="A">
单选框字符串<input type="radio" v-model="gender" value="男">
多行文本框字符串<textarea v-model="text"></textarea>

六、练习题

练习 1:制作一个简单的计算器输入界面

创建两个数字输入框和一个结果显示区域,实时显示两数之和。

提示

data() {
return {
num1: 0,
num2: 0
}
}

在页面上显示 {{ num1 + num2 }}

点击查看完整答案
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>简易计算器</title>
</head>
<body>
<div id="app">
<h2>加法计算器</h2>
<input type="number" v-model.number="num1"> +
<input type="number" v-model.number="num2"> =
<strong>{{ num1 + num2 }}</strong>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
num1: 0,
num2: 0
}
}
}).mount('#app');
</script>
</body>
</html>

注意v-model.number 可以自动将输入转换为数字类型。

练习 2:兴趣爱好选择器

创建一个页面,包含:

  • 多个复选框(至少 4 个爱好选项)
  • 实时显示已选择的爱好数量
  • 如果没选任何爱好,显示提示"请至少选择一项"
点击查看答案
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>兴趣爱好选择</title>
</head>
<body>
<div id="app">
<h2>选择你的兴趣爱好</h2>

<label><input type="checkbox" v-model="hobbies" value="阅读"> 阅读</label><br>
<label><input type="checkbox" v-model="hobbies" value="运动"> 运动</label><br>
<label><input type="checkbox" v-model="hobbies" value="音乐"> 音乐</label><br>
<label><input type="checkbox" v-model="hobbies" value="旅游"> 旅游</label><br>

<p v-if="hobbies.length === 0" style="color: orange;">
⚠️ 请至少选择一项爱好
</p>
<p v-else>
你选择了 {{ hobbies.length }} 项爱好:{{ hobbies.join('、') }}
</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
hobbies: []
}
}
}).mount('#app');
</script>
</body>
</html>

🎉 恭喜你完成第三节课!

下一节我们将学习:事件处理 —— 学会使用 v-on 指令处理用户的点击、输入等操作。