一文搞懂 Vue 模板语法:插值、指令与过滤器的正确用法

我们都知道 Vue 的模板语法是连接数据与视图的核心桥梁,在 HTML 基础上扩展了一套声明式语法,能够以直观的方式将数据渲染到页面,并实现交互逻辑。本文将解析 Vue 模板语法中的插值、指令和过滤器的用法、技巧与避坑点,来掌握 Vue 模板的编写逻辑。有需要的朋友可以来看看呦!

一、插值语法:数据渲染的基础方式

插值是模板中最常用的语法,用于将组件中的响应式数据动态插入到 HTML 中。Vue 提供了多种插值形式,适用于不同的渲染场景。

1. 文本插值({{ }} 双大括号)

基础用法

最常见的插值形式,用于渲染纯文本内容。双大括号内可以直接使用响应式数据、执行简单表达式,但不能编写复杂语句(如 iffor 循环)。

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 处理复杂逻辑,再在模板中引用计算属性。

  • ❌ 不要忽略空值处理:当数据可能为 undefinednull 时,直接渲染会显示 undefinednull 文本。

    正确做法:用 || 或三元表达式设置默认值:

    vue 复制代码
    <div>地址:{{ user.address || '未填写' }}</div>
  • ❌ 不要在插值中使用语句:模板表达式只能是 "有返回值的表达式",不能写 iffor 等语句。

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 指令(简写 :)将响应式数据绑定到属性上(如 srcclassdisabled 等)。

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>

进阶:动态绑定 classstyle
classstyle 是属性绑定的高频场景,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-ifv-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(简写 @

基础用法

用于给元素绑定事件(如 clickinput),语法: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 模板语法是数据驱动视图的核心,掌握以下要点将大幅提升开发效率:

  1. 插值 :用 {{ }} 渲染文本,v-html 渲染 HTML,v-bind 绑定属性;
  2. 指令v-if/v-for 控制渲染,v-on 绑定事件,v-model 双向绑定;
  3. 过滤器 :Vue 2 可用,Vue 3 推荐用 computed 或工具方法替代;
  4. 性能与安全 :避免复杂表达式,谨慎使用 v-html,正确设置 v-forkey

好了,本次分享的Vue 模板语法到此结束!

相关推荐
AryaNimbus12 小时前
你不知道的Cursor系列:使用 Cursor 不会这个超牛 MCP 还没用过吧!
前端·ai编程·cursor
子兮曰12 小时前
🚀2025年Web开发的20大痛点,每一个都让前端想转行!
前端·javascript·浏览器
Propeller12 小时前
【Android】从复用到重绘的控件定制化方式
前端
子兮曰12 小时前
🔥这个Nginx泛域名配置,让90%的开发者直呼"原来还能这样玩!
运维·前端·nginx
前端李二牛12 小时前
我被vite-plugin-style-import硬控了两个小时
前端·vite
huaiqiongying12 小时前
Springboot3+SpringSecurity6Oauth2+vue3前后端分离认证授权-客户端
java·vue.js·spring boot
月弦笙音12 小时前
【Vite】vite常用配置,一篇即可通吃
前端·性能优化·vite
前端很开门13 小时前
程序员的逆天操作,看我如何批量下载iconfont的图标和批量下载 svg 图标
前端·chrome·代码规范
m0_7097886213 小时前
单片机点灯
java·前端·数据库