《Vue3 模板进阶:class/style 绑定、事件对象、修饰符、表单处理与高频易错点》

一、写在前面

学到这里,你应该已经会这些基础能力了:

  • {``{ }} 显示数据

  • v-bind 绑定属性

  • @click 绑定事件

  • v-model 绑定输入框

  • v-if 控制显示隐藏

  • v-for 渲染列表

  • refreactive 定义响应式数据

  • computedwatch 处理派生值和监听逻辑

但在真正写页面的时候,很多人会很快碰到新的问题:

  • 一个按钮选中和未选中,样式怎么动态切换?

  • 行内样式怎么根据数据变化?

  • 点击事件里,怎么拿到事件对象?

  • 提交表单时,为什么页面会刷新?

  • 输入回车时怎么直接触发搜索?

  • 复选框、单选框、下拉框的绑定到底怎么写?

  • 为什么代码能跑,但总觉得写法不规范?

这些问题,表面看起来是零碎细节,

实际上它们非常重要,因为它们决定了你是不是能把页面写得真正"像回事"。

所以这一篇文章,我们就集中解决 Vue3 模板层最常见的一批进阶问题。


二、为什么模板进阶很重要?

很多新手会有一个误区:

觉得只要核心逻辑会了,模板细节问题不大。

其实恰恰相反。

真实项目开发里,很多时间并不是花在"会不会 ref",

而是花在:

  • 样式怎么跟状态联动

  • 表单怎么更规范处理

  • 事件怎么正确拦截和传参

  • 页面交互细节怎么做得自然

也就是说,模板进阶部分不是"边角料",而是你把基础知识真正用起来的重要一步。

如果前面的内容解决的是:

页面能不能跑

那么这一篇更偏向解决:

页面能不能写得更顺、更稳、更接近真实开发


三、动态 class 绑定:为什么页面样式要跟数据联动?

先从最常见的需求开始。

很多页面样式不是固定的,而是跟状态变化有关。比如:

  • 当前按钮是否选中

  • 当前任务是否完成

  • 当前标签页是否激活

  • 某一项是否高亮

  • 某个文本是否显示为红色警告

这时候,你不能把 class 写死。

因为 class 本身也要由数据来决定。

这就是动态 class 绑定要解决的问题。


四、class 绑定的基本写法

Vue 里最常用的是 :class


1. 字符串写法

复制代码
<template>
  <div :class="className">内容区域</div>
</template>

<script setup>
const className = 'box'
</script>

这表示:

  • div 的 class 不再写死

  • 而是由 className 这个变量决定

这种方式最简单,但灵活度一般。


2. 对象写法

这是实际开发中非常常见的方式。

复制代码
<template>
  <div :class="{ active: isActive, danger: isDanger }">内容区域</div>
</template>

<script setup>
import { ref } from 'vue'

const isActive = ref(true)
const isDanger = ref(false)
</script>

这段代码的意思是:

  • 如果 isActivetrue,就加上 active

  • 如果 isDangertrue,就加上 danger

这种写法特别适合"根据布尔状态切换样式"。


3. 数组写法

复制代码
<template>
  <div :class="[classA, classB]">内容区域</div>
</template>

<script setup>
const classA = 'box'
const classB = 'active'
</script>

这表示:

  • 同时绑定多个 class

数组写法适合"多个类名组合"的场景。


五、动态 class 最常见的实战场景

1. 按钮选中状态

复制代码
<template>
  <button :class="{ active: isSelected }" @click="toggleSelect">
    点我切换状态
  </button>
</template>

<script setup>
import { ref } from 'vue'

const isSelected = ref(false)

const toggleSelect = () => {
  isSelected.value = !isSelected.value
}
</script>

<style scoped>
.active {
  background-color: skyblue;
  color: white;
}
</style>

这个例子很经典,因为它直接体现了:

样式不是固定写死的,而是跟状态绑定的。


2. 列表项高亮

复制代码
<template>
  <ul>
    <li
      v-for="item in list"
      :key="item.id"
      :class="{ current: currentId === item.id }"
      @click="currentId = item.id"
    >
      {{ item.name }}
    </li>
  </ul>
</template>

<script setup>
import { ref } from 'vue'

const currentId = ref(1)

const list = ref([
  { id: 1, name: '首页' },
  { id: 2, name: '课程' },
  { id: 3, name: '我的' }
])
</script>

<style scoped>
.current {
  color: red;
  font-weight: bold;
}
</style>

这就是标签页、导航栏、菜单高亮的常见写法。


六、动态 style 绑定:行内样式也能跟着数据变

除了 class,样式也可以直接动态绑定。

Vue 里用的是 :style


1. 对象写法

复制代码
<template>
  <div :style="{ color: textColor, fontSize: fontSize + 'px' }">
    动态样式文本
  </div>
</template>

<script setup>
const textColor = 'blue'
const fontSize = 24
</script>

这里表示:

  • 文字颜色由 textColor 控制

  • 字体大小由 fontSize 控制


2. 适合哪些场景?

style 绑定更适合这种情况:

  • 某个具体样式值是动态变化的

  • 不太适合专门去定义 class

  • 需要根据数据直接计算样式值

比如:

  • 进度条宽度

  • 字体大小

  • 颜色切换

  • 显示/隐藏某些样式数值

例如进度条:

复制代码
<template>
  <div class="bar">
    <div class="inner" :style="{ width: progress + '%' }"></div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const progress = ref(60)
</script>

这里 inner 的宽度会随着 progress 变化。


七、class 和 style 应该怎么选?

这是个很实际的问题。

你可以先记住一个简单原则:

优先用 class

如果只是"几种样式状态切换",更推荐用 class。

因为:

  • 结构更清晰

  • 样式更容易维护

  • 更符合常规开发习惯

style

如果是"某个具体样式值随数据变化",可以用 style。

比如:

  • 宽度 73%

  • 字体大小 18px

  • 背景色来自变量

所以:

  • 状态切换型样式 :更偏 class

  • 数值驱动型样式 :更偏 style


八、事件对象 $event 是什么?

前面你已经会写:

复制代码
<button @click="handleClick">按钮</button>

但很快你就会遇到更具体的问题:

  • 点击的是哪个元素?

  • 鼠标事件里有哪些信息?

  • 输入事件里如何拿到输入值?

  • 我既想传参数,又想拿事件对象怎么办?

这时候就要理解事件对象。


1. 默认事件对象

在原生 JS 里,事件触发时通常都会产生一个事件对象。

Vue 也一样。

例如:

复制代码
<template>
  <button @click="handleClick">点我</button>
</template>

<script setup>
const handleClick = (event) => {
  console.log(event)
}
</script>

这里的 event 就是事件对象。

它里面会包含很多信息,比如:

  • 触发事件的元素

  • 鼠标位置

  • 键盘按键

  • 默认行为相关信息


2. 模板里显式传 $event

如果你还想额外传参数,就可以这样写:

复制代码
<template>
  <button @click="handleClick('张三', $event)">点我</button>
</template>

<script setup>
const handleClick = (name, event) => {
  console.log(name)
  console.log(event)
}
</script>

这里的意思是:

  • 第一个参数是你手动传的 '张三'

  • 第二个参数是当前事件对象

所以 $event 可以理解成:

Vue 模板里当前事件对应的原生事件对象。


九、为什么有时候不直接用 event.target.value

很多输入框示例里,你会看到这种写法:

复制代码
<input @input="handleInput" />

const handleInput = (event) => {
  console.log(event.target.value)
}

这当然没问题。

但在 Vue 里,如果你的目标只是做"输入框值和数据同步",

通常更推荐直接用 v-model

因为:

  • v-model 更简洁

  • 可读性更高

  • 更符合 Vue 思路

所以你可以这样理解:

  • 想自己细控制输入事件,可以用事件对象

  • 只是做值绑定 ,优先考虑 v-model


十、事件修饰符:为什么 Vue 要提供这些语法?

这部分是模板进阶里的高频内容。

很多事件处理,本来需要你手动写一堆原生逻辑。

Vue 为了让模板更简洁,提供了 事件修饰符

它们本质上是在帮你做一些常见事件处理动作。

最常见的有:

  • .stop

  • .prevent

  • .enter


十一、.stop:阻止事件冒泡

先看一个场景:

复制代码
<template>
  <div @click="parentClick">
    父盒子
    <button @click="childClick">子按钮</button>
  </div>
</template>

<script setup>
const parentClick = () => {
  console.log('点击了父盒子')
}

const childClick = () => {
  console.log('点击了子按钮')
}
</script>

如果你点击子按钮,通常会发现:

  • 子按钮事件触发了

  • 父盒子事件也触发了

这是因为事件会冒泡。

如果你不想让子按钮点击继续冒泡到父元素,就可以写:

复制代码
<button @click.stop="childClick">子按钮</button>

.stop 的意思就是:

阻止事件继续向外冒泡。

这是写弹窗、按钮组、嵌套点击区域时非常常见的需求。


十二、.prevent:阻止默认行为

有些 HTML 元素本身带默认行为。

例如:

  • 表单提交会刷新页面

  • 链接点击会跳转

但很多时候你并不想让这个默认行为发生。


1. 表单提交场景

复制代码
<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="username" />
    <button type="submit">提交</button>
  </form>
</template>

<script setup>
import { ref } from 'vue'

const username = ref('')

const handleSubmit = () => {
  console.log('提交内容:', username.value)
}
</script>

这里的 .prevent 表示:

阻止表单原本的刷新提交行为。

这在 Vue 项目里非常常见,因为前端通常想自己控制提交逻辑,而不是让浏览器直接刷新页面。


2. 链接点击场景

复制代码
<a href="https://example.com" @click.prevent="handleClick">点击</a>

这会阻止默认跳转,而改为先执行你自己的逻辑。


十三、.enter:按下回车触发事件

这个修饰符在搜索框、登录框里特别常见。

例如:

复制代码
<template>
  <input v-model="keyword" @keyup.enter="search" placeholder="请输入关键词" />
</template>

<script setup>
import { ref } from 'vue'

const keyword = ref('')

const search = () => {
  console.log('开始搜索:', keyword.value)
}
</script>

这段代码的意思是:

  • 正常输入时不触发搜索

  • 只有按下 Enter 键时才执行 search

所以 .enter 本质上是:

键盘事件的快捷条件筛选。

这会让模板写法比你手动判断 keyCode 更清晰。


十四、表单处理为什么是 Vue 新手高频场景?

因为表单几乎无处不在:

  • 登录

  • 注册

  • 搜索

  • 添加学生

  • 提交评论

  • 创建任务

  • 编辑资料

而 Vue 的一个强项,就是特别适合处理表单和数据联动。

所以表单处理一定是初学阶段要掌握的重点。


十五、输入框、文本域、下拉框的基础绑定

1. 文本输入框

复制代码
<template>
  <input v-model="username" placeholder="请输入用户名" />
  <p>当前输入:{{ username }}</p>
</template>

<script setup>
import { ref } from 'vue'

const username = ref('')
</script>

2. 文本域

复制代码
<template>
  <textarea v-model="content"></textarea>
  <p>{{ content }}</p>
</template>

<script setup>
import { ref } from 'vue'

const content = ref('')
</script>

3. 下拉框

复制代码
<template>
  <select v-model="city">
    <option value="北京">北京</option>
    <option value="上海">上海</option>
    <option value="广州">广州</option>
  </select>

  <p>当前城市:{{ city }}</p>
</template>

<script setup>
import { ref } from 'vue'

const city = ref('北京')
</script>

这些都属于 Vue 表单处理中最基础、最常见的写法。


十六、复选框和单选框怎么理解?

这部分新手也很容易写乱。


1. 单个复选框

复制代码
<template>
  <input type="checkbox" v-model="isAgree" />
  <span>是否同意协议:{{ isAgree }}</span>
</template>

<script setup>
import { ref } from 'vue'

const isAgree = ref(false)
</script>

这里 isAgree 会是布尔值:

  • 勾选为 true

  • 不勾选为 false


2. 单选框

复制代码
<template>
  <input type="radio" value="男" v-model="gender" /> 男
  <input type="radio" value="女" v-model="gender" /> 女

  <p>当前性别:{{ gender }}</p>
</template>

<script setup>
import { ref } from 'vue'

const gender = ref('男')
</script>

这里 gender 会始终是当前选中的那个值。


3. 多个复选框

复制代码
<template>
  <input type="checkbox" value="篮球" v-model="hobbies" /> 篮球
  <input type="checkbox" value="足球" v-model="hobbies" /> 足球
  <input type="checkbox" value="游戏" v-model="hobbies" /> 游戏

  <p>爱好:{{ hobbies }}</p>
</template>

<script setup>
import { ref } from 'vue'

const hobbies = ref([])
</script>

这里 hobbies 会是一个数组,

记录当前勾选了哪些内容。

这一点很重要:

多个复选框配合同一个 v-model 时,通常绑定的是数组。


十七、表单提交时,推荐怎么写?

一个比较标准的新手写法是:

复制代码
<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="username" placeholder="请输入用户名" />
    <input v-model="password" type="password" placeholder="请输入密码" />
    <button type="submit">登录</button>
  </form>
</template>

<script setup>
import { ref } from 'vue'

const username = ref('')
const password = ref('')

const handleSubmit = () => {
  console.log('用户名:', username.value)
  console.log('密码:', password.value)
}
</script>

这个写法有几个优点:

  • 数据来源清晰

  • 表单行为可控

  • 不会刷新页面

  • 后面扩展校验逻辑也方便

这是你以后写登录框、搜索框、录入表单时的常见基础模板。


十八、一个综合案例:搜索框 + 动态样式 + 回车触发

下面给你一个综合点的例子,把这一篇的核心内容串起来。

复制代码
<template>
  <div class="search-box">
    <input
      v-model="keyword"
      @keyup.enter="search"
      :class="{ active: keyword.length > 0 }"
      placeholder="请输入搜索关键词"
    />
    <button @click="search" :style="{ backgroundColor: keyword ? 'skyblue' : '#ccc' }">
      搜索
    </button>
    <p v-if="result">搜索内容:{{ result }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const keyword = ref('')
const result = ref('')

const search = () => {
  if (!keyword.value.trim()) return
  result.value = keyword.value
}
</script>

<style scoped>
.search-box {
  width: 300px;
  margin: 20px auto;
}

.active {
  border: 2px solid skyblue;
}
</style>

这个例子里已经包含了很多模板进阶能力:

  • v-model 绑定输入框

  • @keyup.enter 回车触发

  • 动态 class 控制输入框激活样式

  • 动态 style 控制按钮背景色

  • @click 触发搜索

  • v-if 控制结果显示

如果你能把这种例子真正看懂并自己敲出来,

那你的模板层能力就已经明显进阶了。


十九、新手最常见的高频错误

这一部分非常重要,因为很多错误都不是"不会",而是细节意识还没建立。


1. classstyle 都想靠字符串硬拼

比如把所有动态样式都塞进字符串里,写得非常乱。

前期更推荐优先使用:

  • :class="{ active: isActive }"

  • :style="{ width: progress + '%' }"

这种结构清晰的写法。


2. 表单提交忘记 .prevent

结果一点击提交,页面直接刷新。

新手写表单时,这几乎是最高频错误之一。


3. 想拿输入值,结果忘了 v-model

本来只想做个简单输入联动,却绕去手动找事件对象。

如果目标只是同步值,直接 v-model 往往更合适。


4. 事件传参后,拿不到事件对象

比如你写:

复制代码
<button @click="handleClick('张三')">按钮</button>

然后在函数里还想拿 event,结果发现拿不到。

这时候要显式传 $event

复制代码
<button @click="handleClick('张三', $event)">按钮</button>

5. 样式逻辑和业务逻辑混在一起太乱

比如模板里一口气写非常复杂的判断表达式。

前期建议尽量把模板写清爽,把复杂逻辑留到脚本区或计算属性里处理。


二十、这一篇学完后,你应该达到什么程度?

如果你把这篇真正理解并上手练过,至少应该能做到:

  • 会用 :class 动态切换样式

  • 会用 :style 绑定动态样式值

  • 知道什么时候更适合用 class,什么时候更适合用 style

  • 会在事件处理中拿到事件对象

  • 知道 $event 的作用

  • 会使用 .stop.prevent.enter

  • 会处理输入框、文本域、下拉框、单选框、复选框

  • 会写一个基础但规范的表单提交逻辑

只要这些点打通了,你的 Vue 模板层能力就不再只是"会基础语法",而是开始接近真实页面开发了。


二十一、总结

这一篇文章,我们把 Vue3 模板层中最实用、最容易出问题的一批进阶内容系统讲了一遍。

核心内容可以浓缩成下面几组能力:

  • 动态 class:适合做状态切换类样式

  • 动态 style:适合做数值驱动类样式

  • 事件对象:让你拿到更细的交互信息

  • 事件修饰符:让常见事件处理更简洁

  • 表单处理:是 Vue3 页面开发里的高频核心场景

如果说前面的模板基础,让你开始"能写一个 Vue 页面",

那么这一篇的作用,就是让你开始学会:

把页面写得更灵活、更规范、更接近真实项目。

这一步非常关键,因为你后面学组件化时,很多组件本质上就是这些模板能力的组合和复用。

相关推荐
还是大剑师兰特1 小时前
Vue3 插槽完整实战(具名插槽 + 动态插槽)
前端·javascript·vue.js
fei_sun1 小时前
Vue+SpingBoot+MyBaits框架
前端·javascript·vue.js
爱吃鱼的锅包肉1 小时前
利用css+js实现一个图片随鼠标滑动裁剪的功能
前端·javascript·css·计算机外设
儒雅的烤地瓜2 小时前
小程序 | Vue小程序开发框架:MPvue与UniApp深度解析
前端·vue.js·uni-app·nodejs·cli·mpvue
小鸡脚来咯2 小时前
正则表达式考点
java·开发语言·前端
ujainu2 小时前
Electron 主进程与渲染进程通信详解:HarmonyOS PC基于 `ipcRenderer.send` 与 `ipcMain.on` 的双向数据传输
javascript·electron·harmonyos
Cg136269159742 小时前
JS-对象-
开发语言·javascript·ecmascript
IT_陈寒2 小时前
SpringBoot开发效率提升50%的5个隐藏技巧,官方文档都没告诉你!
前端·人工智能·后端
鹏北海2 小时前
TypeScript 装饰器完全指南
前端·typescript