Vue3 模板语法

发布于:2025-08-31 ⋅ 阅读:(19) ⋅ 点赞:(0)

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)

模板引用