Vue3模板语法

Vue 3 模板语法完全指南

本文档从零开始讲解 Vue 3 的模板语法:模板在哪里、能写什么、不能写什么,以及文本插值、属性绑定、表达式、事件、class/style 等方方面面,配有大量示例,适合新手系统学习。


一、什么是 Vue 的模板?

1.1 模板在哪里?

在 Vue 单文件组件(.vue)里,<template> 标签内的 HTML 就是该组件的模板。

<template>
  <div>
    <p>这里是模板,可以写 HTML + Vue 语法</p>
  </div>
</template>

Vue 会把这些内容编译成“渲染函数”,再根据数据生成真正的 DOM。
所以:模板 = HTML + Vue 专属语法。下面说的“模板语法”,就是指在 <template> 里能用的这些语法。

1.2 模板语法的两大类型

  1. 插值(Interpolation):在 HTML 里“插入”动态数据,最典型的是双花括号 {{ }}
  2. 指令(Directive):以 v- 开头的特殊属性,用来绑定属性、绑定事件、控制显示/循环等。

本文会先讲插值和表达式,再讲属性与事件绑定、class/style、条件与列表,帮你建立完整印象。
指令的详细用法可参考《Vue3指令.md》。


二、文本插值:双花括号 {{ }}

在模板里想显示 JavaScript 变量的值,用双花括号把表达式包起来即可。

2.1 基本用法

<template>
  <div>
    <p>消息:{{ message }}</p>
    <p>数字:{{ count }}</p>
    <p>布尔:{{ isOk }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const message = ref('你好,Vue!')
const count = ref(100)
const isOk = ref(true)
</script>

渲染结果:

  • “消息:你好,Vue!”
  • “数字:100”
  • “布尔:true”

注意:<script setup> 里用 ref 定义的变量,在模板里不用写 .value,Vue 会自动解包。

2.2 表达式可以写什么?

花括号里必须是单个 JavaScript 表达式,不能写语句(如 ifforconst)。

✅ 正确示例:

<template>
  <div>
    <p>{{ message }}</p>
    <p>{{ count + 1 }}</p>
    <p>{{ isOk ? '是' : '否' }}</p>
    <p>{{ message.split('').reverse().join('') }}</p>
    <p>{{ user.name }}</p>
    <p>{{ list.length }}</p>
    <p>{{ fn() }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const message = ref('Hello')
const count = ref(10)
const isOk = ref(true)
const user = ref({ name: '小明' })
const list = ref([1, 2, 3])
function fn() {
  return '函数返回值'
}
</script>

❌ 错误示例:

<!-- 不能写语句 -->
<p>{{ if (ok) { return 'yes' } }}</p>
<p>{{ const a = 1 }}</p>
<p>{{ for (let i of list) {} }}</p>

<!-- 不能写多个语句 -->
<p>{{ count++; count }}</p>

记住:一个表达式 = 一个会产生一个值的式子
赋值、ifforconst/let 都是“语句”,不能直接写在 {{ }} 里。

2.3 插值会转成文本,不会解析 HTML

{{ }} 里的内容会以纯文本形式显示,不会当成 HTML 解析。

<template>
  <p>{{ rawHtml }}</p>
</template>

<script setup>
import { ref } from 'vue'
const rawHtml = ref('<strong>加粗</strong>')
</script>

页面上会显示:
<strong>加粗</strong> 这一串字符,而不是加粗的“加粗”二字。
若需要把字符串当作 HTML 渲染,要使用指令 v-html(详见《Vue3指令.md》),并注意 XSS 安全。


三、在 HTML 属性里“插值”(属性绑定)

在普通 HTML 属性里不能直接写双花括号,写了也不会生效。

<!-- 错误:属性里这样写不会变成动态的 -->
<div id="{{ id }}"></div>
<img src="{{ imgSrc }}">

要让属性值随数据变化,必须用 v-bind(或简写 :)。

3.1 基本绑定:v-bind 或 :

<template>
  <div>
    <div :id="boxId">盒子</div>
    <img :src="imgSrc" :alt="imgAlt" />
    <a :href="url">链接</a>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const boxId = ref('main-box')
const imgSrc = ref('/logo.png')
const imgAlt = ref('Logo')
const url = ref('https://vuejs.org')
</script>

:id 等价于 v-bind:id
属性名前的冒号表示“后面是 JavaScript 表达式”,所以可以写变量、运算、三元等。

3.2 布尔属性

disabledreadonlychecked 这类属性,绑定的值会按“真假”处理:
假值(false、0、”、null、undefined)时,该属性不会出现在 DOM 上。

<template>
  <button :disabled="isDisabled">按钮</button>
  <input type="checkbox" :checked="isChecked" />
</template>

<script setup>
import { ref } from 'vue'
const isDisabled = ref(false)
const isChecked = ref(true)
</script>

3.3 绑定多个属性:无参数 v-bind

若有一整个对象,键是属性名、值是属性值,可以用无参数的 v-bind 一次性绑定。

<template>
  <div v-bind="attrObj">内容</div>
</template>

<script setup>
import { ref } from 'vue'
const attrObj = ref({
  id: 'my-id',
  class: 'box',
  'data-name': 'test'
})
</script>

渲染结果相当于:
<div id="my-id" class="box" data-name="test">内容</div>
后面若对同一个属性又有单独绑定(如 :class="..."),会与 v-bind="attrObj" 合并,单独绑定的优先级更高。


四、模板里能用的 JavaScript 表达式(小结)

以下在插值 {{ }}指令的值(如 :id="..."v-if="...")里都适用。

  • 可用:

    • 变量名、对象属性访问(user.name)、数组下标(list[0]
    • 运算:+ - * / %count + 1
    • 比较:===!==><
    • 逻辑:&&||!
    • 三元:ok ? '是' : '否'
    • 函数调用:fn()message.toUpperCase()
    • 数组/对象字面量(在绑定 class/style 时常用)
  • 不可用:

    • 声明:constletvarfunction
    • 流程控制:ifforwhileswitch
    • 赋值语句:a = 1count++(但 count + 1 这种表达式可以)

另外,模板中的表达式只能访问有限的白名单全局对象(如 MathDate),不能随意访问 windowdocument 等。复杂逻辑应放在组件的 methods / 函数计算属性 里,在模板里只做简单调用。


五、事件绑定:@事件名

在模板里给元素绑定事件,用 v-on:事件名 或简写 @事件名

5.1 内联处理函数与传参

<template>
  <div>
    <button @click="count++">点击 +1</button>
    <p>次数:{{ count }}</p>

    <button @click="sayHi">无参</button>
    <button @click="say('Vue')">传参</button>
    <button @click="(e) => handle(e, 123)">事件对象 + 参数</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
function sayHi() {
  alert('Hi')
}
function say(name) {
  alert('Hello, ' + name)
}
function handle(e, id) {
  console.log(e.target, id)
}
</script>
  • @click="count++":内联表达式,直接执行。
  • @click="sayHi":函数引用,无参时可这样写。
  • @click="say('Vue')":带参数时要写调用,即加括号。
  • 需要事件对象时,在参数里写 $event 或用箭头函数传 e(如上例)。

5.2 事件修饰符(.prevent、.stop、.once 等)

常用写法:在事件名后加 .修饰符,如 @click.prevent@submit.prevent

<template>
  <form @submit.prevent="onSubmit">
    <button type="submit">提交(不刷新页面)</button>
  </form>
  <div @click="onDivClick">
    <button @click.stop="onBtnClick">按钮(不冒泡)</button>
  </div>
  <button @click.once="doOnce">只触发一次</button>
</template>

<script setup>
function onSubmit() {
  console.log('提交')
}
function onDivClick() {
  console.log('div')
}
function onBtnClick() {
  console.log('按钮')
}
function doOnce() {
  console.log('只一次')
}
</script>

更多修饰符(如 .capture.self、按键修饰符)见《Vue3指令.md》。


六、双向绑定:v-model(模板中的写法)

在表单控件上,用 v-model="变量" 即可实现“输入 ↔ 数据”双向同步。
这里只强调模板写法,原理和修饰符见《Vue3指令.md》。

<template>
  <div>
    <input v-model="text" placeholder="请输入" />
    <p>你输入了:{{ text }}</p>

    <input v-model.number="age" type="number" />
    <input v-model.trim="keyword" />
    <textarea v-model.lazy="desc"></textarea>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const text = ref('')
const age = ref(0)
const keyword = ref('')
const desc = ref('')
</script>

七、class 与 style 的绑定(模板语法重点)

class 和 style 除了写死字符串,还可以用对象数组动态绑定,都在模板里用 :class:style 写表达式。

7.1 绑定 class:对象写法

对象的 是类名, 为真时该类名会加上。

<template>
  <div>
    <p :class="{ active: isActive, 'text-danger': hasError }">段落</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
</script>

渲染结果:<p class="active">段落</p>
hasError 为 true,会多一个 text-danger
类名有横线时要加引号,如 'text-danger'

7.2 绑定 class:数组写法

数组里放类名字符串对象,最终会合并成一个 class 列表。

<template>
  <div>
    <p :class="[classA, classB]">段落</p>
    <p :class="[classA, { active: isActive }]">段落</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const classA = ref('title')
const classB = ref('bold')
const isActive = ref(true)
</script>

7.3 绑定 style:对象写法

:style 的值是一个对象,键是 CSS 属性名(驼峰或短横线均可),值是字符串或数字(数字会默认加 px)。

<template>
  <div>
    <p :style="{ color: textColor, fontSize: fontSize + 'px' }">彩色文字</p>
    <p :style="styleObj">另一段</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const textColor = ref('red')
const fontSize = ref(16)
const styleObj = ref({
  backgroundColor: 'lightblue',
  padding: '10px'
})
</script>

7.4 绑定 style:数组写法(多个对象合并)

<template>
  <p :style="[baseStyle, overrideStyle]">文字</p>
</template>

<script setup>
import { ref } from 'vue'
const baseStyle = ref({ color: 'blue', fontSize: '14px' })
const overrideStyle = ref({ fontSize: '20px' })
</script>

后面数组项会覆盖前面同名字段,所以最终 fontSize20px


八、条件渲染:v-if / v-else-if / v-else、v-show

在模板里用指令控制“是否渲染”或“显示/隐藏”。

8.1 v-if、v-else-if、v-else

<template>
  <div>
    <p v-if="score >= 90">优秀</p>
    <p v-else-if="score >= 60">及格</p>
    <p v-else>不及格</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const score = ref(85)
</script>

v-else-ifv-else 必须紧跟在带 v-ifv-else-if 的元素后面,中间不能插其他元素。

8.2 用 template 包一组元素

若要对多个元素一起做条件判断,可以用 <template> 包起来,<template> 不会渲染成真实节点。

<template>
  <template v-if="isLoggedIn">
    <h2>欢迎回来</h2>
    <p>个人中心</p>
  </template>
  <template v-else>
    <p>请先登录</p>
  </template>
</template>

<script setup>
import { ref } from 'vue'
const isLoggedIn = ref(false)
</script>

8.3 v-show

v-show 通过 CSS display 控制显示/隐藏,元素始终在 DOM 里。

<template>
  <p v-show="isVisible">看得见我</p>
</template>

<script setup>
import { ref } from 'vue'
const isVisible = ref(true)
</script>

何时用 v-if、何时用 v-show 见《Vue3指令.md》。


九、列表渲染:v-for

在模板里用 v-for 根据数组或对象循环生成多份 DOM。
必须配合 :key,且 key 要唯一、稳定(通常用 id)。

9.1 遍历数组

<template>
  <ul>
    <li v-for="(item, index) in list" :key="item.id">
      {{ index + 1 }}. {{ item.name }}
    </li>
  </ul>
</template>

<script setup>
import { ref } from 'vue'
const list = ref([
  { id: 1, name: '苹果' },
  { id: 2, name: '香蕉' }
])
</script>

(item, index) 中第一个是当前项,第二个是索引:key="item.id" 不要用 index(在列表会增删时容易出问题)。

9.2 遍历对象

<template>
  <ul>
    <li v-for="(value, key) in user" :key="key">
      {{ key }}: {{ value }}
    </li>
  </ul>
</template>

<script setup>
import { ref } from 'vue'
const user = ref({
  name: '小明',
  age: 18
})
</script>

9.3 用 template 包一层

v-if 一样,可以用 <template> 包住多个元素一起循环。

<template>
  <template v-for="item in list" :key="item.id">
    <h3>{{ item.title }}</h3>
    <p>{{ item.desc }}</p>
  </template>
</template>

十、模板中的其他语法点

10.1 注释

HTML 注释会出现在最终 DOM 里;若不想输出到 DOM,可用 Vue 的注释写法(需在可被 Vue 编译的模板内):

<template>
  <div>
    <!-- 这是普通 HTML 注释,会出现在 DOM -->
    <p>内容</p>
  </div>
</template>

在 Vue 编译的模板里,一般用 <!-- 注释 --> 即可;避免在 {{ }} 或属性值中间写注释。

10.2 大小写

  • HTML 属性、标签名不区分大小写,但推荐小写或短横线(如 my-prop)。
  • 在模板里引用的变量名、方法名要和 <script setup> 里定义的完全一致(区分大小写)。

10.3 避免 v-if 与 v-for 写在同一元素上

当同一节点上同时有 v-ifv-for 时,Vue 3 中 v-if 会先被判断,可能导致逻辑不符合预期。
推荐做法:用 <template> 包一层,或用计算属性先过滤再 v-for

<!-- 不推荐 -->
<li v-for="item in list" v-if="item.show" :key="item.id">...</li>

<!-- 推荐:用 template 包一层 -->
<template v-for="item in list" :key="item.id">
  <li v-if="item.show">...</li>
</template>

十一、模板语法速查表

需求 写法示例
显示变量/表达式 {{ message }}{{ count + 1 }}
属性动态绑定 :id="id"v-bind:src="url"
事件绑定 @click="fn"@click="fn(1)"
双向绑定 v-model="text"v-model.number="n"
条件渲染 v-if / v-else-if / v-elsev-show
列表渲染 v-for="(item, i) in list" :key="item.id"
class 绑定 :class="{ active: isActive }":class="[a, b]"
style 绑定 :style="{ color: c }":style="[a, b]"
不解析 HTML 仅用 {{ }};要解析用 v-html(注意安全)

十二、学习建议

  1. 先练熟{{ }}:属性@事件v-modelv-ifv-for,这些是模板里最常用的。
  2. 牢记:模板里只能写单个表达式,不能写语句;复杂逻辑放到 script 里的函数或计算属性。
  3. 列表必带 :key,且 key 要唯一、稳定。
  4. class/style 多用对象和数组绑定,减少手拼字符串。
  5. 指令的详细说明(修饰符、自定义指令等)见《Vue3指令.md》;插槽见《Vue3插槽.md》。

把本文档里的示例在项目里敲一遍、改一改,会掌握得更快。祝你学习顺利。

发表评论