Vue3 的模板语法是基于 HTML 的扩展,允许开发者声明式地将组件的数据绑定到 DOM 上。它结合了 HTML 的简洁性和 JavaScript 的动态能力,同时提供了一系列指令来处理常见的 DOM 操作场景。
一、文本插值
1. 基本文本绑定
使用双大括号 {{ }}
进行文本插值,将组件的数据实时显示在模板中:
<template> <div> <p>用户名:{{ username }}</p> <p>年龄:{{ user.age }}</p> <p>计算结果:{{ 1 + 2 * 3 }}</p> <p>函数返回值:{{ formatDate(createTime) }}</p> </div> </template> <script setup> import { ref, reactive } from 'vue' const username = ref('张三') const user = reactive({ age: 25 }) const createTime = ref('2023-01-01') function formatDate(date) { return new Date(date).toLocaleString() } </script>
双大括号内可以是:
响应式变量(ref 或 reactive 对象的属性)
JavaScript 表达式(算术运算、三元运算等)
函数调用(返回值会被渲染)
注意:模板表达式只能是单个表达式,不能包含语句(如 if 语句、for 循环)或流程控制。
2. 原始 HTML 绑定
双大括号会将数据解释为普通文本,若需渲染 HTML 字符串,使用 v-html
指令:
<template> <div> <!-- 普通文本绑定(会显示标签) --> <p>{{ rawHtml }}</p> <!-- 渲染为 HTML --> <p v-html="rawHtml"></p> </div> </template> <script setup> import { ref } from 'vue' const rawHtml = ref('<strong style="color: red;">这是加粗的红色文本</strong>') </script>
安全提示:动态渲染 HTML 存在 XSS 风险,避免将用户输入的内容直接通过 v-html
渲染,除非确认内容安全。
3. 一次性插值
使用 v-once
指令可以执行一次性插值,数据变化时,插值处的内容不会更新:
<template> <div> <p v-once>初始值:{{ count }}</p> <p>实时值:{{ count }}</p> <button @click="count++">增加</button> </div> </template> <script setup> import { ref } from 'vue' const count = ref(0) </script>
点击按钮后,v-once
所在的段落内容不会变化,而另一处会实时更新。
二、属性绑定
1. 基本属性绑定
使用 v-bind
指令绑定 HTML 属性到组件数据,缩写为 :
:
<template> <div> <!-- 完整语法 --> <img v-bind:src="imageUrl" alt="示例图片"> <!-- 缩写语法 --> <img :src="imageUrl" :alt="imageAlt"> <!-- 绑定 class 属性 --> <div :class="containerClass">容器</div> <!-- 绑定 style 属性 --> <p :style="textStyle">带样式的文本</p> </div> </template> <script setup> import { ref } from 'vue' const imageUrl = ref('https://picsum.photos/200/300') const imageAlt = ref('风景图片') const containerClass = ref('main-container') const textStyle = ref({ color: 'blue', fontSize: '16px' }) </script>
2. 动态属性名
可以使用方括号 []
绑定动态属性名:
<template> <div> <!-- 属性名由 attributeName 动态决定 --> <div :[attributeName]="value"></div> <button @click="attributeName = 'title'">改为 title 属性</button> <button @click="attributeName = 'data-id'">改为 data-id 属性</button> </div> </template> <script setup> import { ref } from 'vue' const attributeName = ref('title') const value = ref('动态属性值') </script>
3. Class 与 Style 绑定进阶
绑定 class 的多种方式:
<template> <div> <!-- 1. 字符串语法 --> <div :class="basicClass">基础样式</div> <!-- 2. 对象语法(控制是否应用类) --> <div :class="{ active: isActive, 'text-danger': hasError }"> 对象语法示例 </div> <!-- 3. 数组语法(多个类名) --> <div :class="[classA, classB]">数组语法示例</div> <!-- 4. 数组+对象混合(条件应用类) --> <div :class="[isActive ? activeClass : '', errorClass]"> 混合语法示例 </div> </div> </template>
绑定 style 的多种方式:
<template> <div> <!-- 1. 对象语法 --> <div :style="{ color: textColor, fontSize: fontSize + 'px' }"> 样式对象示例 </div> <!-- 2. 使用 reactive 对象 --> <div :style="boxStyle">reactive 样式对象</div> <!-- 3. 数组语法(多个样式对象) --> <div :style="[baseStyle, specialStyle]"> 样式数组示例 </div> </div> </template> <script setup> import { ref, reactive } from 'vue' const textColor = ref('green') const fontSize = ref(16) const boxStyle = reactive({ width: '200px', height: '100px', backgroundColor: '#f0f0f0' }) const baseStyle = ref({ margin: '10px', padding: '5px' }) const specialStyle = ref({ border: '1px solid #ccc' }) </script>
三、事件处理
1. 基本事件绑定
使用 v-on
指令绑定事件,缩写为 @
:
<template> <div> <!-- 完整语法 --> <button v-on:click="handleClick">点击我</button> <!-- 缩写语法 --> <button @click="handleClick">点击我</button> <!-- 直接写表达式 --> <button @click="count++">增加计数 ({{ count }})</button> <!-- 绑定键盘事件 --> <input @keyup="handleKeyUp" placeholder="按任意键"> </div> </template> <script setup> import { ref } from 'vue' const count = ref(0) function handleClick() { alert('按钮被点击了') } function handleKeyUp(event) { console.log('按键:', event.key) } </script>
2. 事件修饰符
Vue 提供了事件修饰符,简化常见的事件处理逻辑:
修饰符 | 说明 |
---|---|
.stop |
阻止事件冒泡 |
.prevent |
阻止默认行为(如表单提交) |
.capture |
使用事件捕获模式 |
.self |
仅当事件目标是元素本身时触发 |
.once |
事件仅触发一次 |
.passive |
优化触摸事件的滚动性能(不阻止默认行为) |
示例:
<template> <div> <!-- 阻止冒泡 --> <div @click="parentClick"> 父元素 <button @click.stop="childClick">子按钮</button> </div> <!-- 阻止默认行为(表单提交) --> <form @submit.prevent="handleSubmit"> <button type="submit">提交(不刷新页面)</button> </form> <!-- 事件仅触发一次 --> <button @click.once="handleOnceClick">只触发一次</button> </div> </template>
3. 按键修饰符
用于键盘事件,过滤特定按键:
<template> <div> <!-- 只有按 Enter 键才触发 --> <input @keyup.enter="submitForm" placeholder="按回车提交"> <!-- 按 Esc 键清空输入 --> <input @keyup.esc="clearInput" v-model="inputValue"> <!-- 系统修饰键(Ctrl、Alt、Shift、Meta) --> <input @keyup.ctrl.67="copyContent" placeholder="Ctrl+C 复制"> </div> </template> <script setup> import { ref } from 'vue' const inputValue = ref('') function submitForm() { console.log('提交表单') } function clearInput() { inputValue.value = '' } function copyContent() { console.log('复制内容') } </script>
常见按键别名:.enter
、.tab
、.delete
、.esc
、.space
、.up
、.down
、.left
、.right
。
四、表单输入绑定
1. 基本用法(v-model)
v-model
指令在表单元素上创建双向数据绑定,自动根据元素类型选择正确的绑定方式:
<template> <form> <!-- 文本输入框 --> <div> <label>用户名:</label> <input type="text" v-model="username"> </div> <!-- 多行文本框 --> <div> <label>简介:</label> <textarea v-model="bio" rows="3"></textarea> </div> <!-- 复选框 --> <div> <label> <input type="checkbox" v-model="isAgreed"> 同意条款 </label> </div> <!-- 多个复选框(绑定数组) --> <div> <label>爱好:</label> <label> <input type="checkbox" v-model="hobbies" value="reading"> 阅读 </label> <label> <input type="checkbox" v-model="hobbies" value="sports"> 运动 </label> </div> <!-- 单选按钮组 --> <div> <label>性别:</label> <label> <input type="radio" v-model="gender" value="male"> 男 </label> <label> <input type="radio" v-model="gender" value="female"> 女 </label> </div> <!-- 下拉选择框 --> <div> <label>城市:</label> <select v-model="city"> <option value="">请选择</option> <option value="beijing">北京</option> <option value="shanghai">上海</option> </select> </div> </form> </template> <script setup> import { ref } from 'vue' const username = ref('') const bio = ref('') const isAgreed = ref(false) const hobbies = ref([]) const gender = ref('') const city = ref('') </script>
2. v-model 修饰符
表单绑定的常用修饰符:
.lazy
:默认实时同步,使用后在 change
事件触发时同步(如输入框失焦时)
.number
:自动将输入值转为数字类型
.trim
:自动过滤输入值的首尾空白字符
<template> <div> <input v-model.lazy="message" placeholder="失去焦点后同步"> <p>消息:{{ message }}</p> <input v-model.number="age" type="text" placeholder="自动转为数字"> <p>年龄类型:{{ typeof age }}</p> <input v-model.trim="username" placeholder="自动去除首尾空格"> <p>用户名长度:{{ username.length }}</p> </div> </template> <script setup> import { ref } from 'vue' const message = ref('') const age = ref(0) const username = ref('') </script>
五、条件渲染
1. v-if / v-else-if / v-else
根据表达式的真假值条件渲染元素,会真正销毁和创建元素:
<template> <div> <button @click="score = Math.floor(Math.random() * 100)"> 随机分数 </button> <p>分数:{{ score }}</p> <div v-if="score >= 90">优秀</div> <div v-else-if="score >= 80">良好</div> <div v-else-if="score >= 60">及格</div> <div v-else>不及格</div> <!-- 在 template 上使用 v-if 条件渲染多个元素 --> <template v-if="isLoggedIn"> <p>欢迎回来,{{ username }}</p> <button @click="isLoggedIn = false">退出登录</button> </template> <template v-else> <p>请登录</p> <button @click="isLoggedIn = true">登录</button> </template> </div> </template> <script setup> import { ref } from 'vue' const score = ref(0) const isLoggedIn = ref(false) const username = ref('张三') </script>
2. v-show
根据表达式的真假值切换元素的显示与隐藏,通过 CSS display
属性实现,元素始终存在于 DOM 中:
<template> <div> <button @click="isVisible = !isVisible"> {{ isVisible ? '隐藏' : '显示' }} </button> <p v-show="isVisible">这段文字会被显示或隐藏</p> </div> </template> <script setup> import { ref } from 'vue' const isVisible = ref(true) </script>
3. v-if 与 v-show 的区别
v-if
是“真正”的条件渲染,条件为假时会销毁元素及事件监听器
v-show
只是简单切换 CSS 的 display
属性,元素始终存在
v-if
有更高的切换开销,适合条件不常变化的场景
v-show
有更高的初始渲染开销,适合需要频繁切换显示状态的场景
六、列表渲染
1. 基本用法(v-for)
使用 v-for
基于数组渲染列表,语法为 item in items
:
<template> <div> <h3>商品列表</h3> <ul> <li v-for="product in products" :key="product.id"> {{ product.name }} - ¥{{ product.price }} </li> </ul> <!-- 获取索引 --> <h3>带索引的列表</h3> <ul> <li v-for="(item, index) in fruits" :key="index"> {{ index + 1 }}. {{ item }} </li> </ul> <!-- 遍历对象 --> <h3>对象属性遍历</h3> <ul> <li v-for="(value, key, index) in userInfo" :key="key"> {{ index + 1 }}. {{ key }}: {{ value }} </li> </ul> </div> </template> <script setup> import { ref, reactive } from 'vue' const products = ref([ { id: 1, name: '手机', price: 3999 }, { id: 2, name: '电脑', price: 5999 } ]) const fruits = ref(['苹果', '香蕉', '橙子']) const userInfo = reactive({ name: '张三', age: 25, city: '北京' }) </script>
2. key 属性的重要性
使用 v-for
时,必须为每项提供唯一的 key
属性:
帮助 Vue 识别每个节点的身份,高效更新 DOM
避免使用索引作为 key(尤其是列表会被修改时),可能导致渲染错误
推荐使用数据中唯一且稳定的标识符(如 id)作为 key
3. 数组更新检测
Vue 能够检测到数组的变更并触发视图更新的方法:
变更方法(会修改原数组):push()
、pop()
、shift()
、unshift()
、splice()
、sort()
、reverse()
替换数组(返回新数组):filter()
、concat()
、slice()
等,需将结果重新赋值给原数组
<template> <div> <ul> <li v-for="item in items" :key="item.id">{{ item.name }}</li> </ul> <button @click="addItem">添加项</button> <button @click="removeItem">移除最后一项</button> <button @click="filterItems">过滤项</button> </div> </template> <script setup> import { ref } from 'vue' const items = ref([ { id: 1, name: '项 1' }, { id: 2, name: '项 2' } ]) function addItem() { // 变更方法 items.value.push({ id: Date.now(), name: `新项 ${items.value.length + 1}` }) } function removeItem() { // 变更方法 items.value.pop() } function filterItems() { // 替换数组 items.value = items.value.filter(item => item.id % 2 === 0) } </script>
七、模板引用
使用 ref
属性获取 DOM 元素或组件实例的引用:
<template> <div> <input ref="inputRef" type="text"> <button @click="focusInput">聚焦输入框</button> <child-component ref="childRef" /> <button @click="callChildMethod">调用子组件方法</button> </div> </template> <script setup> import { ref, onMounted } from 'vue' import ChildComponent from './ChildComponent.vue' // 创建 ref 用于存储引用 const inputRef = ref(null) const childRef = ref(null) // 组件挂载后才能访问到 ref onMounted(() => { inputRef.value.focus() }) function focusInput() { inputRef.value.focus() } function callChildMethod() { // 调用子组件暴露的方法 childRef.value?.childMethod() } </script>
注意:模板引用在组件挂载后才能访问,通常在 onMounted
钩子中使用。
八、总结
Vue3 的模板语法是其核心特性之一,提供了简洁而强大的方式来声明式地绑定数据和处理 DOM 交互。主要包括:
文本插值与原始 HTML 绑定
灵活的属性绑定(包括 class 和 style)
事件处理与修饰符
表单输入的双向绑定(v-model)
条件渲染(v-if / v-show)
列表渲染(v-for)
模板引用