模板语法与常用指令
Vue 使用基于 HTML 的模板语法,将 DOM 声明式地绑定到组件数据。编译后模板会转为高效的虚拟 DOM 渲染函数。本文覆盖 Vue 3 最常用的模板语法和内置指令。
文本插值
用双花括号 {{ }} 渲染文本,支持任意 JavaScript 表达式:
<template>
<p>{{ message }}</p>
<p>{{ count + 1 }}</p>
<p>{{ ok ? '是' : '否' }}</p>
<p>{{ list.join(', ') }}</p>
<p>{{ user.name.toUpperCase() }}</p>
</template>{{ }} 只能包含单个表达式,不能写语句(如 if、for)。复杂逻辑放到 computed 计算属性中。原始 HTML:v-html
{{ }} 会转义 HTML 标签,如需渲染原始 HTML 用 v-html:
<template>
<div v-html="richContent"></div>
</template>
<script setup>
const richContent = '<strong>加粗</strong>文字'
</script>v-html 会导致 XSS 风险,永远不要对用户输入使用 v-html,只用于可信的内容。属性绑定:v-bind / :
用 v-bind(简写 :)将属性值绑定到响应式数据:
<template>
<!-- 绑定单个属性 -->
<img :src="imageUrl" :alt="imageAlt">
<button :disabled="isLoading">提交</button>
<a :href="link" :target="openInNew ? '_blank' : '_self'">链接</a>
<!-- 绑定 class:对象语法 -->
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
<!-- 绑定 class:数组语法 -->
<div :class="[baseClass, isActive ? 'active' : '']"></div>
<!-- 绑定 style -->
<p :style="{ color: textColor, fontSize: fontSize + 'px' }">文字</p>
<!-- 一次绑定多个属性(v-bind 不带参数) -->
<div v-bind="{ id: 'box', class: 'wrapper' }"></div>
</template>
<script setup>
import { ref } from 'vue'
const imageUrl = ref('/logo.png')
const isActive = ref(true)
const hasError = ref(false)
const isLoading = ref(false)
</script>事件监听:v-on / @
用 v-on(简写 @)监听 DOM 事件:
<template>
<!-- 内联处理器 -->
<button @click="count++">点击</button>
<!-- 方法处理器 -->
<button @click="handleClick">点击</button>
<!-- 传参($event 获取原生事件对象) -->
<button @click="handleClick('hello', $event)">点击</button>
<!-- 事件修饰符 -->
<form @submit.prevent="onSubmit">...</form> <!-- 阻止默认行为 -->
<div @click.stop="onClick">...</div> <!-- 阻止冒泡 -->
<div @click.once="onClick">...</div> <!-- 只触发一次 -->
<div @click.self="onClick">...</div> <!-- 只有点击自身触发 -->
<!-- 按键修饰符 -->
<input @keyup.enter="onEnter"> <!-- 按 Enter 触发 -->
<input @keyup.ctrl.enter="onCtrlEnter"> <!-- 按 Ctrl+Enter -->
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function handleClick(msg, event) {
console.log(msg, event)
}
function onSubmit() {
console.log('表单提交')
}
</script>常用事件修饰符速查
| 修饰符 | 说明 |
|---|---|
.prevent | event.preventDefault() |
.stop | event.stopPropagation() |
.once | 只触发一次后移除监听 |
.self | 只在事件源为自身时触发 |
.capture | 使用捕获模式 |
.passive | 提升滚动性能(不能与 .prevent 同用) |
条件渲染:v-if / v-show
<template>
<!-- v-if:条件为 false 时,元素不存在于 DOM -->
<div v-if="status === 'loading'">加载中…</div>
<div v-else-if="status === 'error'">出错了</div>
<div v-else>{{ data }}</div>
<!-- v-show:条件为 false 时,元素存在但 display:none -->
<div v-show="isVisible">常切换的内容</div>
</template>| 对比 | v-if | v-show |
|---|---|---|
| 渲染方式 | 真正销毁/重建 DOM | 切换 display CSS |
| 初始渲染 | 条件为假时不渲染 | 总是渲染 |
| 切换开销 | 高(重新挂载组件) | 低(仅改 CSS) |
| 适用场景 | 条件很少变化 | 频繁切换 |
v-if 和 v-for 同时使用时,v-if 优先级更高(Vue 3)。若需要一起用,把 v-for 放在外层 <template> 上。列表渲染:v-for
<template>
<!-- 遍历数组 -->
<ul>
<li v-for="(item, index) in list" :key="item.id">
{{ index + 1 }}. {{ item.name }}
</li>
</ul>
<!-- 遍历对象 -->
<ul>
<li v-for="(value, key, index) in user" :key="key">
{{ key }}: {{ value }}
</li>
</ul>
<!-- 遍历数字范围 -->
<span v-for="n in 5" :key="n">{{ n }} </span>
<!-- 分组渲染,不产生额外 DOM 节点 -->
<template v-for="item in list" :key="item.id">
<dt>{{ item.name }}</dt>
<dd>{{ item.desc }}</dd>
</template>
</template>:key 是必填项。Vue 通过 key 识别列表中每个节点,缺少 key 会导致渲染错误和性能下降。key 应该是稳定唯一的值(如 id),不要用数组 index(列表有增删时会出错)。表单绑定:v-model
v-model 实现表单元素与数据的双向绑定,是 :value + @input 的语法糖:
<template>
<!-- 文本输入 -->
<input v-model="text" placeholder="请输入">
<p>输入内容:{{ text }}</p>
<!-- 多行文本 -->
<textarea v-model="description"></textarea>
<!-- 复选框(单个,绑定 boolean) -->
<input type="checkbox" v-model="agree"> 同意协议
<!-- 复选框(多个,绑定数组) -->
<input type="checkbox" value="Vue" v-model="selected">
<input type="checkbox" value="React" v-model="selected">
<p>已选:{{ selected }}</p>
<!-- 单选按钮 -->
<input type="radio" value="male" v-model="gender"> 男
<input type="radio" value="female" v-model="gender"> 女
<!-- 下拉选择 -->
<select v-model="city">
<option value="">请选择</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
</template>
<script setup>
import { ref } from 'vue'
const text = ref('')
const description = ref('')
const agree = ref(false)
const selected = ref([])
const gender = ref('')
const city = ref('')
</script>v-model 修饰符
| 修饰符 | 效果 |
|---|---|
.lazy | 改为监听 change 事件(失焦后更新) |
.number | 自动将输入值转为数字 |
.trim | 自动去除首尾空白 |
<input v-model.lazy="text"> <!-- 失焦才更新 -->
<input v-model.number="age"> <!-- 转数字 -->
<input v-model.trim="username"> <!-- 去空格 -->其他常用指令
| 指令 | 说明 | 示例 |
|---|---|---|
v-once | 只渲染一次,不追踪更新 | <span v-once>{{ title }}</span> |
v-pre | 跳过编译,显示原始 Mustache | <span v-pre>{{ raw }}</span> |
v-cloak | 配合 CSS 防止闪烁(SPA 一般不需要) | [v-cloak] { display:none } |
v-memo | 记忆化子树,依赖不变时跳过渲染(性能优化) | <div v-memo="[a, b]"> |
自定义指令
<script setup>
// 局部自定义指令(v-focus)
const vFocus = {
mounted(el) {
el.focus()
}
}
</script>
<template>
<input v-focus placeholder="自动聚焦">
</template>全局注册(在 main.ts 中):
app.directive('focus', {
mounted(el) { el.focus() }
})最后更新于