本节摘要:Vue 用 @ 简写 v-on 声明式绑定事件,用 v-model 实现表单双向绑定——它是"数据决定界面"在输入方向上的便捷封装。本节把事件、事件修饰符、v-model 在各输入上的用法与边界讲透,并与 React 的受控写法对照。
本节阅读目标
阅读完本节,你应当能够:
Vue 用 v-on(简写 @)绑定事件:
<template> <button @click="handleClick">点击</button> </template> <script setup> function handleClick(e) { e.preventDefault(); console.log('点击了', e.target); } </script>
需要冒泡、键盘、鼠标细节时,Vue 提供了事件修饰符:
.stop 阻止冒泡.prevent 阻止默认行为.self 只有自身触发.enter)按需监听按键这些修饰符把"该由开发者写的拦截逻辑"压缩成声明式点缀,比手动判断更省心。
v-model 是 Vue 对"输入即数据"的封装:它等于是"读数据 + 写数据"一体的语法糖。文本、勾选、单选、下拉都能用:
<template> <form @submit.prevent="submit"> <input v-model="name" placeholder="姓名" /> <input type="checkbox" v-model="agree" /> 同意协议 <select v-model="city"> <option value="bj">北京</option> <option value="sh">上海</option> </select> </form> </template> <script setup> import { ref } from 'vue'; const name = ref(''); const agree = ref(false); const city = ref('bj'); function submit() { console.log(name.value, agree.value, city.value); } </script>
写一遍即可,输入变化回写 ref,ref 变化又刷新输入框。这是第 2.1 节"单向"思路在输入方向的便捷化:对单个组件内部,v-model 让你少写一长串双向样板。
v-model 还带几个修饰符,专门处理输入时的高频"脏活":.lazy 让绑定从"每次输入都更新"改为"失焦或回车才更新",.number 自动把字符串转成数字,.trim 去掉首尾空格。它们的意义在于把这些纯逻辑用一行声明替代手写事件判断。
<template> <input v-model.lazy="note" v-model.number="age" v-model.trim="tag" /> </template>
.number 尤其值得记——表单里 <input type="number"> 拿到的往往还是字符串,不转的话用 === 比较长度时就会莫名其妙不成立。有了 v-model.number,这类"类型对不上"的诡异问题少挨一大半。修饰符不是炫技,它们的每个字都在回应用户输入的常见边界。
React 用 value + onChange 手写两路绑定,Vue 用 v-model 打包。本质都是"数据的唯一来源 + 事件把输入写回"。差别只在表达:React 显式、Vue 隐式。拿这个对照表更直观:
| 输入 | React | Vue |
|---|---|---|
| 文本 | value + onChange | v-model |
| 勾选 | checked + onChange | v-model |
| 下拉 | value + onChange | v-model |
| 阻止提交 | preventDefault | @submit.prevent |

受控思路和 v-model 都强调了"值来自数据",当字段变多时,最乏味的是每个输入框都要写 v-model="xx" 加对应 ref。其一省事的写法是让模型的字段名和控件一一对应,并在提交时统一收集——input 只要绑对 v-model 的键,模板就基本是一排自明的声明:
<script setup> import { reactive } from 'vue'; const form = reactive({ name: '', age: '', city: 'bj', agree: false }); function submit() { console.log(form); } // 直接拿到整包干净数据 </script> <template> <input v-model="form.name" placeholder="姓名" /> <input v-model.number="form.age" placeholder="年龄" /> <select v-model="form.city">…</select> </template>
把相关的字段收进一个 form 对象,提交与重置都只需处理这一个对象,多字段表单的"散"就被聚拢成"包"。这跟 4.4 节 reactive 管对象的心智一脉相承——它顺手还让你少写四五个互相独立的 ref。遇到需要初始化或重置整个表单时,把 form 整体替换回默认值即可,比挨个清零干净得多。
Vue 的核心语法在这里收官。你已经能用 Vue 写一个带状态、能接收输入、会响应数据的组件了。让我们在第 5 章把它们进一步串成"组件间的协作艺术"——通信、插槽、样式与复用抽象。