Vue 3 指令完全指南(新手向)
本文档详细介绍 Vue 3 中所有常用指令的用法,配有大量示例,适合零基础学习。
一、什么是指令?
指令(Directive) 是写在 HTML 标签上的、以 v- 开头的特殊属性。Vue 会根据这些指令对 DOM 做“响应式”的更新或绑定事件。
指令长什么样?
<div v-if="isVisible">我会根据 isVisible 显示或隐藏</div>
v-if是指令名"isVisible"是指令的表达式,通常是组件里定义的变量或简单运算
指令的组成(进阶)
一个完整指令可能包含:
- 名称:如
v-if、v-for - 参数:在名称后加冒号,如
v-bind:href里的href - 修饰符:以点开头的后缀,如
v-model.lazy里的.lazy - 值:等号右边的表达式,如
v-if="count > 0"里的count > 0
二、内容渲染类指令
1. v-text —— 纯文本渲染
把变量的纯文本内容写入元素,相当于设置 element.textContent。
语法: v-text="表达式"
<template>
<div>
<p v-text="message"></p>
<!-- 等价于 -->
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('你好,Vue 3!')
</script>
注意: v-text 会覆盖该元素内部的全部内容,一般更推荐用双花括号 {{ }}。
2. v-html —— 原始 HTML 渲染
把变量的值当作 HTML 代码 插入到元素中。
⚠️ 仅在你完全信任内容来源时使用,否则容易导致 XSS 攻击。
语法: v-html="表达式"
<template>
<div>
<div v-html="rawHtml"></div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const rawHtml = ref('<strong>加粗文字</strong> 和 <em>斜体</em>')
</script>
渲染结果: 页面上会显示加粗和斜体,而不是标签字符串。
3. v-show —— 显示/隐藏(切换 CSS display)
根据表达式真假切换元素的 display(true 显示,false 隐藏)。
元素始终留在 DOM 里,只是被隐藏。
语法: v-show="表达式"
<template>
<div>
<button @click="isShow = !isShow">切换显示</button>
<p v-show="isShow">你看得见我!</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const isShow = ref(true)
</script>
适用场景: 频繁切换显示/隐藏时,用 v-show 性能更好(不用反复创建/销毁 DOM)。
4. v-if / v-else-if / v-else —— 条件渲染(是否渲染到 DOM)
根据条件决定是否把这块 DOM 渲染出来。为 false 时,对应的节点不会出现在 DOM 中。
语法:
v-if="表达式"v-else-if="表达式"(可选,必须紧跟在v-if或v-else-if后面)v-else(可选,不需要值,必须紧跟在v-if或v-else-if后面)
<template>
<div>
<p v-if="score >= 90">优秀!</p>
<p v-else-if="score >= 60">及格</p>
<p v-else>不及格</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const score = ref(85)
</script>
v-if 和 v-show 怎么选?
| 对比 | v-if | v-show |
|---|---|---|
| 机制 | 不满足条件时不渲染 DOM | 始终渲染,用 CSS 隐藏 |
| 切换成本 | 有销毁/创建成本 | 只改 display,成本低 |
| 适用 | 不常切换的条件块 | 频繁显示/隐藏 |
三、列表渲染:v-for
根据数据源循环渲染一组元素。
语法:
v-for="(项, 索引?) in 数组" 或
v-for="(值, 键?) in 对象"
遍历数组
<template>
<ul>
<li v-for="(item, index) in list" :key="item.id">
{{ index + 1 }}. {{ item.name }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const list = ref([
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
])
</script>
重要: 使用 v-for 时务必加 :key,且 key 要唯一(通常用 id)。这样 Vue 才能正确做 diff 和复用,避免渲染错乱。
遍历对象
<template>
<ul>
<li v-for="(value, key) in user" :key="key">
{{ key }}: {{ value }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const user = ref({
name: '小明',
age: 18,
city: '北京'
})
</script>
遍历数字范围
<template>
<span v-for="n in 5" :key="n">{{ n }}</span>
<!-- 渲染 1 2 3 4 5 -->
</template>
与 v-if 一起用
不推荐在同一个元素上写 v-for 和 v-if。需要“按条件过滤列表”时,用计算属性先过滤,再对结果做 v-for。
<template>
<ul>
<li v-for="item in activeList" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
<script setup>
import { computed, ref } from 'vue'
const list = ref([
{ id: 1, name: '任务A', done: false },
{ id: 2, name: '任务B', done: true }
])
const activeList = computed(() => list.value.filter(item => !item.done))
</script>
四、属性绑定:v-bind(可简写为 :)
把数据绑定到元素的属性上,属性值会随数据变化而更新。
语法: v-bind:属性名="表达式" 或简写 :属性名="表达式"
绑定普通属性
<template>
<div>
<img :src="imgUrl" :alt="imgAlt" />
<a :href="link">点击跳转</a>
</div>
</template>
<script setup>
import { ref } from 'vue'
const imgUrl = ref('/logo.png')
const imgAlt = ref('网站 Logo')
const link = ref('https://vuejs.org')
</script>
绑定 class(对象/数组写法)
<template>
<div>
<!-- 对象:键为类名,值为是否添加 -->
<p :class="{ active: isActive, 'text-danger': hasError }">段落</p>
<!-- 数组 -->
<p :class="[classA, classB]">段落</p>
<!-- 数组 + 对象混合 -->
<p :class="[classA, { active: isActive }]">段落</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
const classA = ref('title')
const classB = ref('bold')
</script>
绑定 style(对象/数组)
<template>
<div>
<p :style="{ color: textColor, fontSize: fontSize + 'px' }">彩色文字</p>
<p :style="styleObj">另一段</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const textColor = ref('red')
const fontSize = ref(16)
const styleObj = ref({
backgroundColor: 'lightblue',
padding: '10px'
})
</script>
五、事件绑定:v-on(可简写为 @)
给元素绑定事件,如点击、输入、键盘等。
语法: v-on:事件名="处理函数或内联语句" 或 @事件名="..."
基本用法
<template>
<div>
<button @click="count++">点击了 {{ count }} 次</button>
<button @click="sayHello">打招呼</button>
<button @click="greet('Vue')">传参</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function sayHello() {
alert('你好!')
}
function greet(name) {
alert('你好,' + name + '!')
}
</script>
访问原生事件对象:$event
<template>
<button @click="handleClick($event)">点击</button>
</template>
<script setup>
function handleClick(e) {
console.log(e.target) // 被点击的 DOM 元素
}
</script>
常用事件修饰符
| 修饰符 | 作用说明 |
|---|---|
.prevent |
调用 event.preventDefault(),阻止默认行为(如表单提交、链接跳转) |
.stop |
调用 event.stopPropagation(),阻止冒泡 |
.once |
事件最多触发一次 |
.capture |
使用捕获阶段 |
.self |
仅当事件来自当前元素自身时才触发 |
<template>
<div>
<!-- 阻止默认行为:比如提交表单时不刷新页面 -->
<form @submit.prevent="onSubmit">
<button type="submit">提交</button>
</form>
<!-- 阻止冒泡:点击按钮时不会触发外层 div 的点击 -->
<div @click="onDivClick">
<button @click.stop="onBtnClick">按钮</button>
</div>
<!-- 只触发一次 -->
<button @click.once="doOnce">只执行一次</button>
</div>
</template>
<script setup>
function onSubmit() {
console.log('表单提交了')
}
function onDivClick() {
console.log('div 被点了')
}
function onBtnClick() {
console.log('按钮被点了')
}
function doOnce() {
console.log('只会出现一次')
}
</script>
按键修饰符
<template>
<input
type="text"
@keyup.enter="onEnter"
@keyup.esc="onEsc"
/>
</template>
<script setup>
function onEnter() {
console.log('按下了回车')
}
function onEsc() {
console.log('按下了 Esc')
}
</script>
六、双向绑定:v-model
在表单控件上实现双向绑定:数据变化会更新视图,用户输入也会更新数据。
语法: v-model="变量"
(本质是 :value + @input 的语法糖,不同控件内部事件可能不同)
文本框
<template>
<div>
<input v-model="username" type="text" placeholder="请输入用户名" />
<p>你输入的是:{{ username }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const username = ref('')
</script>
多行文本
<template>
<textarea v-model="intro" placeholder="个人简介"></textarea>
<p>{{ intro }}</p>
</template>
<script setup>
import { ref } from 'vue'
const intro = ref('')
</script>
复选框(单个 / 多个)
<template>
<div>
<!-- 单个:绑定布尔值 -->
<label>
<input type="checkbox" v-model="agree" />
我同意协议
</label>
<p>同意状态:{{ agree }}</p>
<!-- 多个:绑定数组 -->
<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 agree = ref(false)
const hobbies = ref([])
</script>
单选框
<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>
下拉选择
<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>
v-model 修饰符
| 修饰符 | 作用 |
|---|---|
.lazy |
在 change 时再同步(如失焦、选择完成),而不是每次 input 都同步 |
.number |
尽量把输入转成数字类型 |
.trim |
自动去掉首尾空格 |
<template>
<div>
<!-- 失焦时才更新 -->
<input v-model.lazy="message" />
<!-- 转为数字 -->
<input v-model.number="age" type="number" />
<!-- 去除首尾空格 -->
<input v-model.trim="keyword" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('')
const age = ref(0)
const keyword = ref('')
</script>
七、插槽:v-slot(可简写为 #)
用于在子组件里预留“洞”,由父组件传入 HTML 或组件。
插槽名默认是 default,具名插槽可写 v-slot:名字 或 #名字。
语法:
v-slot:插槽名="作用域"- 简写:
#插槽名="作用域" - 默认插槽:
v-slot="作用域"或#default="作用域"
默认插槽
子组件:
<!-- Child.vue -->
<template>
<div class="card">
<slot></slot>
</div>
</template>
父组件:
<template>
<Child>
<p>这里的内容会出现在子组件的 slot 位置</p>
</Child>
</template>
具名插槽
子组件:
<!-- Layout.vue -->
<template>
<div class="layout">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
父组件:
<template>
<Layout>
<template #header>
<h1>页面标题</h1>
</template>
<p>主体内容</p>
<template #footer>
<p>版权信息</p>
</template>
</Layout>
</template>
作用域插槽(子传数据给父)
子组件通过 <slot :变量名="值"> 把数据传出去,父组件用 v-slot="scope" 接收。
子组件:
<!-- UserList.vue -->
<template>
<ul>
<li v-for="user in users" :key="user.id">
<slot :user="user">{{ user.name }}</slot>
</li>
</ul>
</template>
<script setup>
defineProps(['users'])
</script>
父组件:
<template>
<UserList :users="users">
<template #default="{ user }">
<strong>{{ user.name }}</strong> - {{ user.age }} 岁
</template>
</UserList>
</template>
<script setup>
import { ref } from 'vue'
const users = ref([
{ id: 1, name: '小明', age: 18 },
{ id: 2, name: '小红', age: 20 }
])
</script>
八、其他内置指令
1. v-pre —— 不编译
该元素及其子元素不会被 Vue 编译,直接输出原始 Mustache 和指令。
<template>
<div v-pre>
{{ 这里的双花括号会原样显示 }}
<span v-if="false">这里的 v-if 也不会生效</span>
</div>
</template>
用途: 展示代码示例、避免误编译。
2. v-once —— 只渲染一次
元素只渲染一次,之后数据再变也不会更新。
<template>
<p v-once>初始值:{{ msg }}</p>
<!-- 后续修改 msg,这段文字不会变 -->
</template>
<script setup>
import { ref } from 'vue'
const msg = ref('只显示这一次')
</script>
用途: 静态、不需要响应的内容,可略省性能。
3. v-memo(Vue 3.2+)—— 条件性跳过更新
只有依赖的数组里的值变化时,才重新渲染该元素及其子节点。
语法: v-memo="[依赖1, 依赖2, ...]"
<template>
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>{{ item.name }}</p>
</div>
</template>
用途: 大列表优化,减少不必要的子树更新。
4. v-cloak —— 避免未编译模板闪现
在 Vue 接管前,模板会短暂以原始 {{ }} 形式显示。配合 CSS 可隐藏未编译内容,等编译完成后再显示。
<style>
[v-cloak] {
display: none;
}
</style>
<template>
<div v-cloak>
{{ message }}
</div>
</template>
九、自定义指令(了解即可)
除了内置指令,还可以用 app.directive() 或 const vXxx = { ... } 注册自定义指令,用来直接操作 DOM。
注册全局自定义指令
// main.js
const app = createApp(App)
app.directive('focus', {
mounted(el) {
el.focus()
}
})
在组件内注册局部指令
<script setup>
const vFocus = {
mounted(el) {
el.focus()
}
}
</script>
<template>
<input v-focus />
</template>
钩子函数(常用)
created:元素创建时beforeMount:挂载前mounted:挂载后(常用,可操作 DOM)beforeUpdate/updated:更新前后beforeUnmount/unmounted:卸载前后
示例:自定义 v-color
<template>
<p v-color="'red'">红色文字</p>
<p v-color="color">动态颜色</p>
</template>
<script setup>
import { ref } from 'vue'
const color = ref('blue')
const vColor = {
mounted(el, binding) {
el.style.color = binding.value
},
updated(el, binding) {
el.style.color = binding.value
}
}
</script>
十、指令速查表
| 指令 | 作用简述 |
|---|---|
v-text |
设置元素文本内容 |
v-html |
插入原始 HTML(慎用) |
v-show |
根据条件切换 display 显示/隐藏 |
v-if / v-else-if / v-else |
条件渲染,不满足则不渲染 DOM |
v-for |
列表渲染,需配合 :key |
v-on / @ |
绑定事件 |
v-bind / : |
绑定属性、class、style |
v-model |
表单双向绑定 |
v-slot / # |
插槽:父传内容给子、或接收子组件作用域 |
v-pre |
不编译,原样输出 |
v-once |
只渲染一次 |
v-memo |
依赖不变时跳过更新 |
v-cloak |
配合 CSS 避免未编译模板闪现 |
十一、学习建议
- 先练熟:
v-if、v-for、v-bind、v-on、v-model,这些用得最多。 - 列表一定加
:key,且 key 要稳定、唯一。 v-if和v-show分清:少切换用v-if,频繁切换用v-show。- 表单用
v-model,记得用.lazy、.number、.trim等修饰符。 - 组件化时再深入
v-slot和自定义指令。
把本文档里的示例在本地跑一遍,改一改数据与条件,会掌握得更快。祝你学习顺利!