我们都知道 Vue 的模板语法是连接数据与视图的核心桥梁,在 HTML 基础上扩展了一套声明式语法,能够以直观的方式将数据渲染到页面,并实现交互逻辑。本文将解析 Vue 模板语法中的插值、指令和过滤器的用法、技巧与避坑点,来掌握 Vue 模板的编写逻辑。有需要的朋友可以来看看呦!
一、插值语法:数据渲染的基础方式
插值是模板中最常用的语法,用于将组件中的响应式数据动态插入到 HTML 中。Vue 提供了多种插值形式,适用于不同的渲染场景。
1. 文本插值({{ }}
双大括号)
基础用法 :
最常见的插值形式,用于渲染纯文本内容。双大括号内可以直接使用响应式数据、执行简单表达式,但不能编写复杂语句(如 if
、for
循环)。
vue
<template>
<!-- 1. 直接渲染响应式数据 -->
<div>用户名:{{ username }}</div>
<!-- 2. 支持简单表达式(算术运算、三元判断、字符串拼接) -->
<div>年龄:{{ age + 1 }}</div>
<div>是否成年:{{ age >= 18 ? '是' : '否' }}</div>
<div>欢迎语:{{ 'Hello, ' + username + '!' }}</div>
<!-- 3. 调用组件内方法(注意性能问题) -->
<div>注册时间:{{ formatTime(registerTime) }}</div>
</template>
<script setup>
import { ref } from 'vue';
const username = ref('张三');
const age = ref(20);
const registerTime = ref(1690000000000);
// 时间格式化方法
const formatTime = (timestamp) => {
return new Date(timestamp).toLocaleString();
};
</script>
避坑指南:
-
❌ 不要在插值中编写复杂逻辑(如多步计算、数据过滤),会导致模板臃肿且影响性能(组件重渲染时会重复执行)。
正确做法:用
computed
处理复杂逻辑,再在模板中引用计算属性。 -
❌ 不要忽略空值处理:当数据可能为
undefined
或null
时,直接渲染会显示undefined
或null
文本。正确做法:用
||
或三元表达式设置默认值:vue<div>地址:{{ user.address || '未填写' }}</div>
-
❌ 不要在插值中使用语句:模板表达式只能是 "有返回值的表达式",不能写
if
、for
等语句。
2. HTML 插值(v-html
指令)
基础用法 :
当需要渲染包含 HTML 标签的字符串(如后端返回的富文本)时,{{ }}
会将 HTML 转义为纯文本,此时需用 v-html
指令渲染原始 HTML。
vue
<template>
<!-- 错误:双大括号会转义 HTML,显示 "<p>这是富文本</p>" -->
<div>{{ richText }}</div>
<!-- 正确:v-html 渲染原始 HTML -->
<div v-html="richText"></div>
</template>
<script setup>
import { ref } from 'vue';
// 后端返回的富文本内容
const richText = ref('<p style="color: red; font-size: 16px;">这是带样式的文本</p>');
</script>
安全警告 :
v-html
存在严重的 XSS 攻击风险!如果渲染的内容来自用户输入或不可信来源(如恶意用户注入 <script>alert('攻击')</script>
),会导致跨站脚本攻击。
✅ 安全原则:
-
仅对可信内容 使用
v-html
; -
禁止在用户可编辑的内容(如评论、私信)中使用
v-html
。
其他注意事项:
v-html
渲染的 HTML 不会被 Vue 编译,内部的 Vue 指令(如v-if
、@click
)无法生效;- 样式隔离:
v-html
内容的样式会受全局 CSS 影响,若需隔离,可使用scoped
样式 +::v-deep
穿透。
3. 属性插值(v-bind
指令,简写 :
)
基础用法 :
双大括号不能用于 HTML 属性赋值,需用 v-bind
指令(简写 :
)将响应式数据绑定到属性上(如 src
、class
、disabled
等)。
vue
<template>
<!-- 1. 绑定普通属性 -->
<img :src="avatarUrl" :alt="username + '的头像'">
<div :id="'user-' + userId" :data-role="userRole"></div>
<!-- 2. 绑定布尔属性(值为 true 时添加属性,false 时移除) -->
<button :disabled="isDisabled">提交</button>
<input type="checkbox" :checked="isAgreed">
</template>
<script setup>
import { ref } from 'vue';
const avatarUrl = ref('https://example.com/avatar.png');
const username = ref('张三');
const userId = ref(123);
const userRole = ref('admin');
const isDisabled = ref(true); // 按钮禁用
const isAgreed = ref(false); // 复选框未选中
</script>
进阶:动态绑定 class
与 style
class
和 style
是属性绑定的高频场景,Vue 支持对象和数组两种语法,满足动态样式需求。
(1)动态绑定 class
-
对象语法 :适合 "控制单个 / 多个类名是否添加",键为类名,值为布尔值(
true
添加,false
移除)。 -
数组语法:适合 "从多个类名中选择",数组元素为类名字符串或响应式变量。
vue
<template>
<!-- 对象语法:active 类是否添加取决于 isActive,disabled 同理 -->
<div class="base" :class="{ active: isActive, disabled: isDisabled }">
动态类名
</div>
<!-- 数组语法:从多个类中选择一个(根据 isLarge 切换) -->
<div :class="[ baseClass, isLarge ? 'large' : 'small' ]">
数组语法类名
</div>
</template>
<style>
.base { padding: 10px; }
.active { color: red; }
.disabled { background: #eee; }
.large { font-size: 20px; }
.small { font-size: 14px; }
</style>
<script setup>
import { ref } from 'vue';
const isActive = ref(true);
const isDisabled = ref(false);
const baseClass = ref('container');
const isLarge = ref(true);
</script>
(2)动态绑定 style
-
对象语法 :键为 CSS 属性(支持驼峰式
fontSize
或短横线式font-size
),值为样式值。 -
数组语法:用于合并多个样式对象。
vue
<template>
<!-- 对象语法:动态设置字体大小、颜色 -->
<div :style="{ fontSize: size + 'px', color: textColor }">
动态样式
</div>
<!-- 数组语法:合并基础样式与主题样式 -->
<div :style="[ baseStyle, themeStyle ]">
合并样式
</div>
</template>
<script setup>
import { ref } from 'vue';
const size = ref(16);
const textColor = ref('#333');
const baseStyle = ref({ padding: '10px', margin: '5px' });
const themeStyle = ref({ border: '1px solid #ccc', borderRadius: '4px' });
</script>
二、指令语法:控制视图逻辑的核心工具
Vue 指令是带有 v-
前缀的特殊属性,用于在模板中实现条件渲染、循环、事件绑定等逻辑。指令的核心作用是 "将数据逻辑映射到视图行为"。
1. 条件渲染:v-if
/v-else-if
/v-else
基础用法 :
根据表达式的布尔值决定元素是否 "存在于 DOM 中"(不是隐藏,而是挂载或销毁),支持多条件判断。
vue
<template>
<div v-if="role === 'admin'">
管理员视图:<button>删除数据</button>
</div>
<div v-else-if="role === 'editor'">
编辑者视图:<button>修改内容</button>
</div>
<div v-else>
普通用户视图:仅查看
</div>
</template>
<script setup>
import { ref } from 'vue';
const role = ref('editor'); // 渲染编辑者视图
</script>
进阶:用 <template>
批量控制元素
若需条件渲染多个元素且不想添加额外父容器,可将 v-if
放在 <template>
标签上(<template>
是虚拟容器,不会渲染到 DOM)。
vue
<template>
<template v-if="showContent">
<h3>文章标题</h3>
<p>文章内容...</p>
<div>作者信息</div>
</template>
<div v-else>内容已隐藏</div>
</template>
<script setup>
import { ref } from 'vue';
const showContent = ref(true);
</script>
v-if
vs v-show
:核心区别
特性 | v-if |
v-show |
---|---|---|
DOM 存在性 | false 时元素销毁 |
始终存在,通过 display: none 隐藏 |
切换成本 | 高(挂载 / 销毁,触发生命周期) | 低(仅修改 CSS) |
初始渲染成本 | false 时无成本 |
无论条件如何,均需渲染 |
适用场景 | 低频切换(如权限控制、弹窗) | 高频切换(如标签页、折叠面板) |
避坑点:
- ❌ 不要在同一元素上同时使用
v-if
和v-for
(Vue 3 会报警告)。v-for
优先级高于v-if
,会导致先循环所有元素再过滤,浪费性能。
正确做法:用<template>
包裹v-for
,或先通过computed
过滤数据。
2. 循环渲染:v-for
基础用法 :
根据数组或对象生成重复元素,语法:
-
遍历数组:
v-for="(item, index) in array"
(item
为元素,index
为索引) -
遍历对象:
v-for="(value, key, index) in object"
(value
为值,key
为键) -
遍历整数:
v-for="num in 5"
(生成 5 个元素)
必须配合 key
属性使用 ,key
用于帮助 Vue 识别元素唯一性,优化渲染性能。
vue
<template>
<!-- 1. 遍历数组(推荐用 item.id 作为 key) -->
<ul>
<li v-for="(item, index) in goodsList" :key="item.id">
{{ index + 1 }}. {{ item.name }} - {{ item.price }}元
</li>
</ul>
<!-- 2. 遍历对象 -->
<div v-for="(value, key) in userInfo" :key="key">
{{ key }}: {{ value }}
</div>
</template>
<script setup>
import { ref } from 'vue';
const goodsList = ref([
{ id: 1, name: '手机', price: 3999 },
{ id: 2, name: '电脑', price: 5999 }
]);
const userInfo = ref({ name: '张三', age: 20, gender: '男' });
</script>
key
属性的正确用法:
-
✅
key
必须唯一:使用后端返回的id
等唯一标识,避免重复。 -
✅
key
必须稳定:不要用index
作为key
(列表增删时index
会变化,导致元素频繁销毁 / 创建)。
vue
<!-- 错误:用 index 作为 key -->
<li v-for="(item, index) in list" :key="index">...</li>
<!-- 正确:用唯一 id 作为 key -->
<li v-for="item in list" :key="item.id">...</li>
3. 事件绑定:v-on
(简写 @
)
基础用法 :
用于给元素绑定事件(如 click
、input
),语法:v-on:事件名="处理函数"
或简写 @事件名="处理函数"
。支持传递参数和获取原生事件对象。
vue
<template>
<!-- 1. 无参数:自动接收事件对象 $event -->
<button @click="handleClick">点击我</button>
<!-- 2. 带参数:手动传递 $event 获取事件对象 -->
<div @click="handleItemClick(item.id, $event)">
{{ item.name }}
</div>
</template>
<script setup>
import { ref } from 'vue';
const item = ref({ id: 1, name: '商品A' });
// 无参数处理函数
const handleClick = (e) => {
console.log('点击事件对象:', e.target);
};
// 带参数处理函数
const handleItemClick = (id, e) => {
console.log('商品ID:', id);
e.stopPropagation(); // 阻止事件冒泡
};
</script>
事件修饰符:简化事件处理
Vue 提供了常用事件修饰符,避免在函数中编写原生代码(如 e.stopPropagation()
):
修饰符 | 作用 | 示例 |
---|---|---|
.stop |
阻止事件冒泡 | @click.stop="handleClick" |
.prevent |
阻止默认行为(如表单提交) | @submit.prevent="handleSubmit" |
.self |
仅元素自身触发时执行 | @click.self="handleClick" |
.once |
事件仅触发一次 | @click.once="handleClick" |
示例:
vue
<!-- 阻止表单默认提交(不刷新页面) -->
<form @submit.prevent="handleSubmit">
<button type="submit">提交</button>
</form>
<!-- 点击一次后失效 -->
<button @click.once="handleClickOnce">仅点击一次</button>
4. 双向绑定:v-model
基础用法 :
v-model
是语法糖,结合 v-bind
(绑定值)和 v-on
(监听输入),实现 "数据↔视图" 双向同步,常用于表单元素。
vue
<template>
<!-- 文本输入框 -->
<input type="text" v-model="username">
<p>你输入的是:{{ username }}</p>
<!-- 复选框(单个绑定布尔值,多个绑定数组) -->
<input type="checkbox" v-model="isAgreed"> 同意协议
<div>
<input type="checkbox" value="apple" v-model="fruits"> 苹果
<input type="checkbox" value="banana" v-model="fruits"> 香蕉
</div>
<!-- 单选框 -->
<div>
<input type="radio" value="male" v-model="gender"> 男
<input type="radio" value="female" v-model="gender"> 女
</div>
</template>
<script setup>
import { ref } from 'vue';
const username = ref('');
const isAgreed = ref(false);
const fruits = ref([]); // 多个复选框绑定数组
const gender = ref('male');
</script>
v-model
修饰符:
-
.lazy
:失去焦点或按回车时同步数据(默认实时同步)。 -
.number
:自动将输入值转为数字类型。 -
.trim
:自动去除首尾空格。
vue
<input v-model.lazy="username"> <!-- 失去焦点同步 -->
<input v-model.number="age"> <!-- 转为数字 -->
<input v-model.trim="phone"> <!-- 去除空格 -->
5. 其他常用指令
指令 | 作用 | 示例 |
---|---|---|
v-pre |
跳过编译,显示原始内容(如 {{ }} ) |
<div v-pre>{{ 不解析 }}</div> |
v-cloak |
隐藏未编译的插值(解决闪烁问题) | <div v-cloak>{{ message }}</div> |
v-once |
仅渲染一次,后续数据变化不更新 | <div v-once>{{ title }}</div> |
三、过滤器(Vue 2 特性,Vue 3 已移除)
过滤器用于在模板中格式化数据(如时间、金额),Vue 2 支持全局和局部过滤器,但 Vue 3 已移除该功能(推荐用 computed
或方法替代)。
Vue 2 过滤器用法
1. 局部过滤器(组件内生效) :
在组件的 filters
选项中定义,仅在当前组件使用。
vue
<template>
<div>时间:{{ createTime | formatTime }}</div>
<div>金额:{{ price | formatMoney(2) }}</div>
</template>
<script>
export default {
data() {
return { createTime: 1690000000000, price: 99.5 };
},
filters: {
// 时间格式化
formatTime(timestamp) {
return new Date(timestamp).toLocaleDateString();
},
// 金额格式化(保留 n 位小数)
formatMoney(amount, n = 2) {
return amount.toFixed(n);
}
}
};
</script>
2. 全局过滤器(全项目生效) :
在 main.js
中通过 Vue.filter()
定义,所有组件可使用。
vue
// main.js(Vue 2)
import Vue from 'vue';
Vue.filter('formatTime', (timestamp) => {
return new Date(timestamp).toLocaleString();
});
Vue 3 替代方案
Vue 3 推荐用计算属性 或工具方法替代过滤器,更灵活且性能更好:
vue
<template>
<!-- 用计算属性格式化 -->
<div>时间:{{ formattedTime }}</div>
<!-- 用工具方法格式化 -->
<div>金额:{{ formatMoney(price, 2) }}</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { formatMoney } from '@/utils/format'; // 导入工具方法
const createTime = ref(1690000000000);
const price = ref(99.5);
// 计算属性(适合依赖响应式数据的格式化)
const formattedTime = computed(() => {
return new Date(createTime.value).toLocaleString();
});
</script>
四、总结
Vue 模板语法是数据驱动视图的核心,掌握以下要点将大幅提升开发效率:
- 插值 :用
{{ }}
渲染文本,v-html
渲染 HTML,v-bind
绑定属性; - 指令 :
v-if
/v-for
控制渲染,v-on
绑定事件,v-model
双向绑定; - 过滤器 :Vue 2 可用,Vue 3 推荐用
computed
或工具方法替代; - 性能与安全 :避免复杂表达式,谨慎使用
v-html
,正确设置v-for
的key
。
好了,本次分享的Vue 模板语法到此结束!