指令(Directives)是 Vue 模板中最具标志性的功能,它们以 v- 开头,本质上是模板语法糖------让你在 HTML 中直接操作 DOM 状态。
结合你目前的项目(<script setup> + TypeScript),我把 Vue 3 最核心的指令和事件处理分成 "数据渲染" 、"条件/循环" 、"事件/表单" 和 "高级性能" 四大板块来讲,重点拆解你在实战中最容易踩坑的地方。
1. 数据渲染:v-bind(动态属性)与 v-html
v-bind(简写 :)
这是最常用的指令,用来动态绑定 HTML 属性。
<script setup lang="ts">
const imgUrl = 'https://example.com/logo.png'
const isDisabled = true
const customId = 'main-box'
</script>
<template>
<!-- 标准写法 -->
<img v-bind:src="imgUrl" />
<!-- 简写(99% 的情况都这么写) -->
<img :src="imgUrl" />
<!-- 动态绑定布尔值属性 -->
<button :disabled="isDisabled">不可点击</button>
<!-- 动态绑定多个属性(直接传入对象) -->
<div :id="customId" :class="{ active: isActive }"></div>
</template>
💡 动态参数(高级) :
v-bind:[attributeName]="value",属性名本身可以是 JS 表达式。
v-html(谨慎使用)
用于插入原始 HTML 片段(会覆盖子元素)。
<template>
<div v-html="rawHtml"></div>
</template>
⚠️ 安全警告 :千万不要 在用户输入的内容上使用 v-html,极易导致 XSS 攻击。只用于你完全可控的富文本数据。
2. 条件渲染:v-if vs v-show
| 指令 | 原理 | 适用场景 |
|---|---|---|
v-if |
"真实销毁/重建" 。若为 false,该元素及其事件监听器不会存在于 DOM 中。 |
初始渲染条件不确定,或切换成本较高(如复杂的组件)。 |
v-show |
"CSS 显隐" 。元素始终存在,只是切换 display: none。 |
频繁切换显示/隐藏(如下拉菜单、Tab 切换)。 |
<template>
<!-- v-if 配套 v-else-if / v-else -->
<div v-if="status === 'loading'">加载中...</div>
<div v-else-if="status === 'error'">出错了</div>
<div v-else>内容加载完毕</div>
<!-- v-show 不支持 v-else -->
<div v-show="isVisible">我频繁切换,用 v-show 更省性能</div>
</template>
3. 🚀 列表渲染:v-for(高频考点)
v-for 用于遍历数组或对象。必须搭配 :key,这是 Vue 进行虚拟 DOM 对比(Diff 算法)的唯一标识。
遍历数组(最常用)
<script setup lang="ts">
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]
</script>
<template>
<!-- (item, index) 两个参数 -->
<li v-for="(user, index) in users" :key="user.id">
{{ index + 1 }} - {{ user.name }}
</li>
</template>
遍历对象
<template>
<li v-for="(value, key, index) in myObject" :key="key">
{{ key }}: {{ value }}
</li>
</template>
遍历数字范围
<template>
<span v-for="n in 10" :key="n">{{ n }}</span> <!-- 输出 1 到 10 -->
</template>
4. ⚠️ 致命陷阱:v-for 与 v-if 的优先级
在 Vue 3 中,v-if 的优先级高于 v-for。
这意味着,如果你在同一个元素上同时写这两个指令,v-if 无法访问 v-for 作用域里的变量(因为 v-if 先执行,此时循环变量还不存在)。
❌ 错误写法(会报错):
<!-- 报错:item 在 v-if 中未定义 -->
<li v-for="item in list" v-if="item.isActive" :key="item.id">{{ item.name }}</li>
✅ 正确写法(使用 <template> 包装):
<template v-for="item in list" :key="item.id">
<li v-if="item.isActive">{{ item.name }}</li>
</template>
更推荐的写法是使用计算属性先过滤数据,这样模板更干净、性能更好。
5. 事件处理:v-on(简写 @)
这是你代码里 @update="handleChildEmit" 的底层原理。
基本用法与传参
<script setup lang="ts">
// TypeScript 类型注解:事件对象是 MouseEvent
const handleClick = (event: MouseEvent) => {
console.log(event.clientX)
}
const handleSubmit = (msg: string, event: MouseEvent) => {
console.log(msg, event.target)
}
</script>
<template>
<!-- 1. 不传参,默认接收原生事件对象 -->
<button @click="handleClick">点击</button>
<!-- 2. 传参 + 保留原生事件对象(必须用 $event 占位) -->
<button @click="handleSubmit('Hello', $event)">传参点击</button>
</template>
事件修饰符(非常实用,减少 JS 代码量)
| 修饰符 | 作用 | 示例 |
|---|---|---|
.stop |
阻止事件冒泡(event.stopPropagation()) |
@click.stop="doThis" |
.prevent |
阻止默认行为(event.preventDefault()) |
@submit.prevent="onSubmit" |
.once |
事件只触发一次 | @click.once="doOnce" |
.self |
只有 event.target 是自身时才触发 |
@click.self="doThat" |
.capture |
事件监听器使用捕获模式 |
链式调用 :
@click.stop.prevent="doIt"(阻止冒泡并阻止默认行为)。
按键修饰符(键盘事件)
<!-- 只有按下 Enter 键才触发 -->
<input @keyup.enter="submit" />
<!-- 只有按下 Ctrl + S 触发 -->
<input @keyup.ctrl.s="save" />
6. 双向绑定 v-model 的进阶(修饰符)
你在代码中使用了 v-model="modelValue",这里补充 3 个修饰符:
| 修饰符 | 作用 |
|---|---|
.lazy |
将 input 事件改为 change 事件(失焦时再更新,提升大文本输入性能) |
.number |
自动将用户输入转为数字类型 |
.trim |
自动过滤首尾空格 |
<template>
<input v-model.number="age" type="number" />
<input v-model.trim="username" />
</template>
7. 性能优化:v-once 与 v-memo
-
v-once:渲染一次后,该元素及其子元素不再随数据变化而更新(适合静态内容)。<div v-once>{{ 巨大的静态数据 }}</div> -
v-memo(Vue 3.2+):缓存特定依赖项的渲染结果,只有依赖项变化时才重新渲染。<div v-memo="[item.id, item.name]"> <!-- 只有 item.id 或 item.name 变化时才更新 --> </div>
8. 在你当前代码中的实际映射
回顾你的 父组件 模板,你已经用了:
v-model="modelValue":双向绑定。@update="handleChildEmit":事件处理(v-on简写)。@click="callChildMethod":点击事件传参。
你可以尝试将 v-for 加入练习,比如在父组件中加一个数组:
const demoList = ref(['Vue', 'React', 'Angular'])
然后在模板里用 v-for 渲染,观察加上 :key 和不加的区别(打开浏览器控制台看 Vue 的警告)。
总结速记口诀(帮你记忆)
- 属性绑定 写
:,事件绑定 写@。 v-if切成本高 ,v-show切频繁。v-for必带:key,不要与v-if同行。- 事件修饰符链式写 ,
$event传原生对象。
如果有哪个指令你觉得自己还没完全摸透,或者想让我结合某个具体业务场景(比如表格渲染、表单验证)再展开讲讲,随时告诉我! 😄