Vue3表单

Vue 3 表单完全指南

本文档从零开始讲解 Vue 3 的表单:如何用 v-model 绑定各类输入框、单选、多选、下拉框,如何提交表单、做简单校验,以及 .lazy / .number / .trim 等修饰符,配有大量示例,适合新手系统学习。


一、为什么 Vue 里要专门学“表单”?

1.1 表单里有什么?

页面上和用户“输入、选择”相关的,几乎都是表单相关元素:

  • 文本框<input type="text"><textarea>
  • 单选<input type="radio">
  • 多选<input type="checkbox">
  • 下拉<select><option>

在 Vue 里,我们不会手写 document.querySelector 去拿这些值,而是用 数据v-model双向绑定:用户改输入,数据变;我们改数据,输入框显示也跟着变。
所以“Vue 表单”的核心就是:每种控件怎么用 v-model、绑定的值是什么类型、怎么一起提交和校验


二、文本框(input text / textarea)

2.1 单行文本:input type=”text”

绑定一个 字符串 即可。
用户输入什么,变量就是什么;给变量赋值,输入框就显示什么。

<template>
  <div>
    <input v-model="username" type="text" placeholder="请输入用户名" />
    <p>你输入了:{{ username }}</p>
  </div>
</template>

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

注意: 不要同时写 v-model:value,v-model 已经包含了“绑定 value + 监听 input”的含义,再写 :value 会冲突。

2.2 多行文本:textarea

用法和单行一样,v-model 绑一个字符串。
在 Vue 里,textarea 的内容用 v-model 控制,不要在 <textarea> 中间写静态内容。

<template>
  <div>
    <textarea v-model="intro" placeholder="个人简介" rows="4"></textarea>
    <p>预览:{{ intro }}</p>
  </div>
</template>

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

2.3 密码框、数字框

type=”password”type=”number” 等,同样是 v-model 绑字符串(或数字,见下文 .number):

<input v-model="password" type="password" placeholder="密码" />
<input v-model="age" type="number" min="0" max="120" />

若希望 age 在 script 里是数字类型,可加 v-model.number(见“修饰符”一节)。


三、单选与多选

3.1 单个复选框(绑定布尔值)

一个 checkbox,勾选为 true,不勾选为 false:

<template>
  <label>
    <input type="checkbox" v-model="agree" />
    我同意协议
  </label>
  <p>同意状态:{{ agree }}</p>
</template>

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

3.2 多个复选框(绑定数组)

多个 checkbox 对应“多选”,每个选项有一个 value,v-model 绑一个数组,选中的项的 value 会出现在数组里。

<template>
  <div>
    <label><input type="checkbox" v-model="hobbies" value="读书" /> 读书</label>
    <label><input type="checkbox" v-model="hobbies" value="运动" /> 运动</label>
    <label><input type="checkbox" v-model="hobbies" value="音乐" /> 音乐</label>
    <p>选中的爱好:{{ hobbies }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const hobbies = ref([])
</script>
  • hobbies 必须是数组(如 ref([]))。
  • 每个 checkbox 都要有 value,选中的 value 会加入数组,取消则从数组移除。

3.3 单选框(绑定单个值)

多个 radio 为一组,同一个 v-model 绑一个字符串(或数字),选中的那个的 value 就是变量的值。

<template>
  <div>
    <label><input type="radio" v-model="gender" value="男" /> 男</label>
    <label><input type="radio" v-model="gender" value="女" /> 女</label>
    <p>性别:{{ gender }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const gender = ref('男')
</script>
  • 同一组的 radio 必须绑同一个 v-model 变量。
  • 每个 value 决定“选中时赋给变量的值”,可以是字符串或数字。

四、下拉选择(select)

4.1 单选下拉

v-model 绑一个字符串(或数字),值为当前选中的 value
若没有选任何一项,通常给一个空字符串的 option(如“请选择”)。

<template>
  <div>
    <select v-model="selectedCity">
      <option value="">请选择城市</option>
      <option value="北京">北京</option>
      <option value="上海">上海</option>
      <option value="广州">广州</option>
    </select>
    <p>选中:{{ selectedCity }}</p>
  </div>
</template>

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

4.2 多选下拉(multiple)

selectmultiple,v-model 绑数组,选中的多个 option 的 value 会组成数组。

<template>
  <select v-model="selectedIds" multiple>
    <option value="1">选项一</option>
    <option value="2">选项二</option>
    <option value="3">选项三</option>
  </select>
  <p>选中:{{ selectedIds }}</p>
</template>

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

4.3 选项来自数据(v-for)

实际项目中,选项往往来自接口或列表,用 v-for 生成 option 即可;value:value 绑定数据:

<template>
  <select v-model="selectedId">
    <option value="">请选择</option>
    <option v-for="item in options" :key="item.id" :value="item.id">
      {{ item.name }}
    </option>
  </select>
</template>

<script setup>
import { ref } from 'vue'
const selectedId = ref('')
const options = ref([
  { id: 1, name: '苹果' },
  { id: 2, name: '香蕉' },
  { id: 3, name: '橙子' }
])
</script>

五、v-model 修饰符(.lazy / .number / .trim)

5.1 .lazy —— 失焦或回车后再同步

默认 v-model 是“每输入一个字符就同步一次”。
加上 .lazy 后,改为在 change 时再同步(如失焦、或下拉选完),减少更新次数,适合“不必实时、只要最终值”的场景。

<template>
  <input v-model.lazy="message" placeholder="失焦后才会同步" />
  <p>当前值:{{ message }}</p>
</template>

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

5.2 .number —— 转为数字

希望输入框的值在 script 里是数字类型时,可加 .number
Vue 会尝试把输入转成数字(转不成则仍是字符串)。

<template>
  <input v-model.number="age" type="number" />
  <p>年龄类型:{{ typeof age }}</p>
</template>

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

5.3 .trim —— 去掉首尾空格

自动去掉用户输入的首尾空格,中间空格保留。

<template>
  <input v-model.trim="keyword" placeholder="前后空格会被去掉" />
</template>

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

5.4 修饰符可组合

<input v-model.lazy.trim="name" />
<input v-model.number.trim="count" type="number" />

六、表单提交与数据汇总

6.1 用 @submit.prevent 阻止默认提交

表单的 submit 会触发表单默认行为(刷新整页、跳转等)。
在 Vue 里一般用 @submit.prevent 阻止默认,再在方法里用已绑定的数据发请求或做校验。

<template>
  <form @submit.prevent="onSubmit">
    <input v-model="form.username" placeholder="用户名" />
    <input v-model="form.password" type="password" placeholder="密码" />
    <button type="submit">登录</button>
  </form>
</template>

<script setup>
import { ref } from 'vue'
const form = ref({
  username: '',
  password: ''
})
function onSubmit() {
  console.log('提交数据:', form.value)
  // 这里发请求:axios.post('/api/login', form.value)
}
</script>

6.2 用一个对象统一管理表单字段

把表单里所有要提交的字段放在一个 refreactive 里,提交时直接取这个对象,方便维护和校验:

<template>
  <form @submit.prevent="onSubmit">
    <input v-model="form.name" placeholder="姓名" />
    <input v-model="form.age" type="number" placeholder="年龄" />
    <select v-model="form.city">
      <option value="">请选择</option>
      <option value="北京">北京</option>
      <option value="上海">上海</option>
    </select>
    <label>
      <input type="checkbox" v-model="form.agree" /> 同意协议
    </label>
    <button type="submit">提交</button>
  </form>
</template>

<script setup>
import { reactive } from 'vue'
const form = reactive({
  name: '',
  age: null,
  city: '',
  agree: false
})
function onSubmit() {
  console.log('表单数据:', { ...form })
}
</script>

七、简单校验示例

校验可以在 提交时 做:根据 form 里的值判断,不通过就提示并 return,不继续提交。

<template>
  <form @submit.prevent="onSubmit">
    <input v-model="form.name" placeholder="姓名" />
    <p v-if="errors.name" class="error">{{ errors.name }}</p>
    <input v-model.number="form.age" type="number" placeholder="年龄" />
    <p v-if="errors.age" class="error">{{ errors.age }}</p>
    <button type="submit">提交</button>
  </form>
</template>

<script setup>
import { reactive, ref } from 'vue'
const form = reactive({
  name: '',
  age: null
})
const errors = ref({})
function onSubmit() {
  errors.value = {}
  if (!form.name.trim()) {
    errors.value.name = '请输入姓名'
  }
  if (form.age == null || form.age === '' || form.age < 0 || form.age > 150) {
    errors.value.age = '请输入有效年龄(0-150)'
  }
  if (Object.keys(errors.value).length > 0) return
  console.log('校验通过,提交:', form)
}
</script>

<style scoped>
.error { color: red; font-size: 12px; }
</style>

八、常见场景示例

8.1 登录表单(用户名 + 密码 + 记住我)

<template>
  <form @submit.prevent="login">
    <input v-model.trim="username" placeholder="用户名" />
    <input v-model="password" type="password" placeholder="密码" />
    <label>
      <input type="checkbox" v-model="remember" /> 记住我
    </label>
    <button type="submit">登录</button>
  </form>
</template>

<script setup>
import { ref } from 'vue'
const username = ref('')
const password = ref('')
const remember = ref(false)
function login() {
  console.log({ username: username.value, password: password.value, remember: remember.value })
}
</script>

8.2 注册表单(多字段 + 重复密码校验)

<template>
  <form @submit.prevent="register">
    <input v-model.trim="form.username" placeholder="用户名" />
    <input v-model="form.password" type="password" placeholder="密码" />
    <input v-model="form.password2" type="password" placeholder="确认密码" />
    <p v-if="form.password && form.password !== form.password2" class="error">两次密码不一致</p>
    <input v-model.trim="form.email" type="email" placeholder="邮箱" />
    <button type="submit">注册</button>
  </form>
</template>

<script setup>
import { reactive } from 'vue'
const form = reactive({
  username: '',
  password: '',
  password2: '',
  email: ''
})
function register() {
  if (form.password !== form.password2) return alert('两次密码不一致')
  console.log('注册数据:', { ...form })
}
</script>

<style scoped>
.error { color: red; }
</style>

8.3 搜索框(回车搜索 + .trim)

<template>
  <input
    v-model.trim="keyword"
    type="text"
    placeholder="输入关键词按回车搜索"
    @keyup.enter="search"
  />
  <button @click="search">搜索</button>
</template>

<script setup>
import { ref } from 'vue'
const keyword = ref('')
function search() {
  if (!keyword.value) return
  console.log('搜索:', keyword.value)
}
</script>

8.4 筛选表单(多条件,提交时一起用)

<template>
  <form @submit.prevent="onFilter">
    <input v-model="filter.keyword" placeholder="关键词" />
    <select v-model="filter.category">
      <option value="">全部分类</option>
      <option value="A">分类A</option>
      <option value="B">分类B</option>
    </select>
    <label><input type="checkbox" v-model="filter.onlyInStock" /> 仅显示有货</label>
    <button type="submit">筛选</button>
  </form>
</template>

<script setup>
import { reactive } from 'vue'
const filter = reactive({
  keyword: '',
  category: '',
  onlyInStock: false
})
function onFilter() {
  console.log('筛选条件:', { ...filter })
}
</script>

九、易错点与注意点

9.1 多选(checkbox 组)必须绑数组

多个 checkbox 用同一个 v-model 时,变量必须是 数组(如 ref([])),否则无法正确收集多个选中项。

9.2 不要同时写 v-model 和 :value / @input

v-model 本质是 :value + @input(或对应控件的 value/change)。
同一元素上不要再写 :value@input,否则会覆盖或冲突。

9.3 select 的“请选择”用 value=””

若希望初始是“未选”,给一个 ,且 v-model 初始值设为 ;若选项来自接口,要等数据到了再绑,否则可能选不中。

9.4 提交前可再拷贝一份数据

提交时若直接传 form 对象,若后面还要清空表单,注意引用关系;必要时 JSON.parse(JSON.stringify(form)){ …form } 提交副本,避免把响应式对象直接发给后端带来意外。


十、表单控件与 v-model 值类型速查表

控件 v-model 绑定的值类型 说明
input text / textarea 字符串 可加 .trim、.lazy
input number 字符串或数字 建议 .number 得到数字
单个 checkbox 布尔值 勾选 true,不勾选 false
多个 checkbox 数组 选中项的 value 组成数组
radio 字符串(或数字) 选中项的 value
select 单选 字符串(或数字) 选中 option 的 value
select 多选 数组 选中 option 的 value 数组

十一、学习建议

  1. 先练熟每种控件:text / textarea → 单 checkbox → 多 checkbox → radio → select,各自绑什么类型(字符串、布尔、数组)。
  2. 表单数据用一个对象(ref/reactive)统一管理,提交时用 @submit.prevent 在方法里取这个对象。
  3. 校验在 onSubmit 里做,用单独一个 errors 对象存错误信息,模板里用 v-if 显示。
  4. 善用 .lazy.number.trim,按需组合。

把本文档里的示例在项目里敲一遍、改一改,会掌握得更牢。事件修饰符见《Vue3事件处理.md》,v-model 原理见《Vue3指令.md》。祝你学习顺利。

发表评论