
本篇定位模板语法是 Vue 的视图层语言是你每天编写 Vue 代码时打交道最多的部分。本篇将系统讲解 Vue 模板中的所有语法规则——从简单的文本插值到强大的指令系统让你彻底掌握如何把数据变成用户看到的界面。读完本篇你将能熟练阅读和编写任何 Vue 模板代码。一、模板语法概览Vue 使用一种基于 HTML 的模板语法它允许你声明式地将组件实例的数据JavaScript 状态绑定到 DOM页面结构上。底层原理上Vue 会将你的模板编译成高效的 JavaScript 渲染函数并利用响应式系统在数据变化时智能地重新渲染。简单来说你只需要在模板中声明你想要的结果Vue 会在背后自动完成所有 DOM 操作。Vue 模板的两种编写方式方式说明适用场景声明式模板SFC 中的template基于 HTML 扩展包含指令和插值绝大多数场景直观、易读、可维护性强渲染函数render/h函数使用 JavaScript 编程式地描述 VNode需要高度动态或逻辑复杂的场景本系列教程默认使用声明式模板这也是 Vue 官方推荐的主流写法。Vue 模板中的所有动态能力都可以归结为两大类插值Interpolation—— 在 HTML 内容中嵌入动态数据指令Directives—— 以v-开头的特殊属性为 HTML 元素赋予动态行为下面我们逐项深入学习。二、文本插值{{ }}Mustache 语法最基本的数据绑定形式是文本插值使用双大括号{{ }}官方名称Mustache 语法。script setup langts import { ref } from vue const message ref(Hello, Vue 3!) const count ref(42) const user ref({ name: 张三, age: 25 }) /script template div !-- 基本文本插值 -- p{{ message }}/p p计数器当前值{{ count }}/p !-- 访问对象属性 -- p姓名{{ user.name }}年龄{{ user.age }}/p !-- 插值中可以执行表达式见后文详解 -- p明年年龄{{ user.age 1 }}/p /div /template核心特征{{ }}中的内容会被 Vue 解析为 JavaScript 表达式不是语句当绑定的数据发生变化时插值内容会自动更新响应式插值只能用在文本节点中不能用在 HTML 属性中属性绑定需要用v-bind2.1 插值中的表达式规则{{ }}中只能使用单个 JavaScript 表达式Expression不能使用语句Statement。✅ 正确表达式p{{ number 1 }}/p p{{ isActive ? 已激活 : 未激活 }}/p p{{ message.split().reverse().join() }}/p p{{ user.name.toUpperCase() }}/p❌ 错误语句!-- 赋值语句 -- p{{ let a 1 }}/p !-- 流程控制 -- p{{ if (isActive) { return 激活 } }}/p !-- 声明变量 -- p{{ const message hello }}/p 设计原理Vue 模板中的表达式在沙箱环境中执行只能访问受限的全局变量列表如Math、Date等无法访问window对象。这意味着你无法在模板中调用window.alert()或访问自定义全局变量——这是出于安全考虑强制你将逻辑放在组件代码中。2.2 插值的 HTML 安全性Vue 默认会对插值内容进行HTML 转义以防止 XSS跨站脚本攻击script setup const dangerousHtml ref(scriptalert(恶意代码)\/script) /script template !-- 会被渲染为纯文本字符串脚本不会执行 -- p{{ dangerousHtml }}/p !-- 输出lt;scriptgt;alert(恶意代码)lt;/scriptgt; -- /template如果你明确知道内容是安全的 HTML 并希望直接渲染需要使用v-html指令见下文。三、原始 HTMLv-html指令当你需要渲染包含 HTML 标签的字符串时使用v-html指令script setup langts import { ref } from vue const rawHtml ref(span stylecolor: red; font-weight: bold;这是红色的加粗文字/span) /script template div !-- 使用 v-html 渲染原始 HTML -- div v-htmlrawHtml/div !-- 对比使用插值只会显示纯文本 -- div{{ rawHtml }}/div !-- 输出lt;span stylecolor: red;gt;这是红色的加粗文字lt;/spangt; -- /div /template⚠️ 安全警告v-html的内容会直接插入为 HTML不会经过 Vue 的模板编译即其中的{{ }}和指令不会被解析永远不要在v-html中使用用户提供的内容如 URL 参数、表单输入等这会导致严重的 XSS 安全漏洞仅在内容完全可信如你自己编写的富文本内容时使用v-html最佳实践优先使用组件化渲染如渲染函数或 VNode替代v-html这样既能保留 Vue 的响应式能力又能保证安全。四、属性绑定v-bind指令HTML 属性不能使用{{ }}插值需要使用v-bind指令来动态绑定属性值。4.1 基本用法script setup langts import { ref } from vue const dynamicId ref(main-header) const imageUrl ref(https://example.com/logo.png) const isDisabled ref(true) const placeholderText ref(请输入用户名) /script template !-- 完整写法v-bind:属性名表达式 -- div v-bind:iddynamicId内容区域/div !-- 简写语法最常用:属性名表达式 -- img :srcimageUrl altLogo / input :disabledisDisabled :placeholderplaceholderText / /template渲染结果dividmain-header内容区域/divimgsrchttps://example.com/logo.pngaltLogo/inputdisabledplaceholder请输入用户名/4.2 动态绑定多个属性对象语法如果你有一个包含多个属性的对象可以使用v-bind的对象语法一次性全部绑定script setup import { ref } from vue const attrs ref({ id: submit-btn, class: btn primary, disabled: false, data-track: click-submit // 带连字符的属性名需要用引号 }) /script template button v-bindattrs提交/button /template渲染结果buttonidsubmit-btnclassbtn primarydata-trackclick-submit提交/button这在封装组件时非常有用如透传未知属性给子组件。4.3 布尔类型属性的特殊处理对于disabled、readonly、checked、selected等布尔属性v-bind的行为与原生 HTML 一致值为真值true、非空字符串、对象等时属性被添加值为假值false、null、undefined、空字符串时属性被移除script setup const isActive ref(true) const isEditable ref(false) const emptyString ref() /script template button :disabledisActive禁用状态/button !-- 渲染button disabled禁用状态/button -- input :readonlyisEditable / !-- 渲染input / readonly 属性不存在 -- input :disabledemptyString / !-- 渲染input disabled / 空字符串是真值 -- /template⚠️ 常见陷阱空字符串在 JavaScript 中是真值truthy所以:disabled会添加disabled属性。如果要表示不添加该属性请使用null或undefined。4.4 动态绑定 class 和 style高频场景class和style是开发中最常绑定的属性Vue 为它们提供了专门的增强语法。绑定 class① 对象语法根据条件动态切换类名script setup import { ref } from vue const isActive ref(true) const hasError ref(false) const isLarge ref(true) // 可以用计算属性或普通对象 const classObject { active: true, text-danger: false, size-large: true } /script template !-- 单个类名动态切换 -- div :class{ active: isActive }内容/div !-- 渲染div classactive内容/div -- !-- 多个类名组合 -- div :class{ active: isActive, text-danger: hasError, size-large: isLarge }状态组件/div !-- 渲染div classactive size-large状态组件/div -- !-- 对象在代码中定义推荐复杂逻辑时使用 -- div :classclassObject复杂状态/div /template② 数组语法绑定多个类名script setup const isActive ref(true) const activeClass ref(active) const errorClass ref(text-danger) const baseClass btn const isDisabled ref(false) /script template div :class[baseClass, activeClass, errorClass]按钮/div !-- 渲染div classbtn active text-danger按钮/div -- !-- 数组 对象混合 -- div :class[baseClass, { active: isActive, disabled: isDisabled }]混合/div /template绑定 style① 对象语法绑定内联样式script setup const activeColor ref(red) const fontSize ref(16) const styleObject ref({ color: blue, backgroundColor: #f0f0f0, padding: 10px }) const baseStyle ref({ color: blue, backgroundColor: #f0f0f0, padding: 10px }) const overrideStyle ref({ color: red, backgroundColor: #f0f0f0, padding: 10px }) /script template !-- 直接绑定属性 -- div :style{ color: activeColor, fontSize: fontSize px }文字/div !-- 绑定样式对象 -- div :stylestyleObject样式对象/div !-- 数组语法多个样式对象合并 -- div :style[baseStyle, overrideStyle]合并样式/div /template② 自动添加浏览器前缀Vue 会自动检测并添加必要的浏览器前缀如-webkit-、-moz-你无需手动处理。③ 多值语法Vue 3.5 支持div :style{ display: [-webkit-box, -ms-flexbox, flex] }/divVue 会按顺序尝试直到浏览器支持为止。五、JavaScript 表达式支持Vue 模板中所有数据绑定位置{{ }}和指令的属性值都支持完整的 JavaScript 表达式。5.1 支持的表达式类型script setup import { ref } from vue const count ref(10) const price ref(99.9) const names ref([Alice, Bob, Charlie]) const user ref({ firstName: 张, lastName: 三 }) const now new Date() /script template !-- 算术运算 -- p{{ count 5 }}/p !-- 三元运算符 -- p{{ count 10 ? 大于10 : 小于等于10 }}/p !-- 方法调用注意每次渲染都会重新执行慎用 -- p{{ price.toFixed(2) }}/p !-- 数组方法 -- p{{ names.join( - ) }}/p !-- 对象解构 -- p{{ user.firstName user.lastName }}/p !-- 逻辑运算 -- div :class[isActive active, isDisabled || enabled]逻辑/div /template5.2 访问全局 APIVue 模板中可访问的全局变量是受限的主要包括Math、Date、Array、Object、String、Number、Boolean等原生构造器Infinity、undefined、NaN、isFinite、isNaN、parseFloat、parseInt、decodeURI、decodeURIComponent、encodeURI、encodeURIComponent如果需要访问window上的自定义全局变量需要在组件中显式暴露。5.3 设计哲学为什么限制表达式复杂度Vue 推荐复杂逻辑放在计算属性或方法中而不是写在模板里。原因有三可读性模板应该清晰直观一眼能看出 UI 结构可测试性计算属性和方法可以独立编写单元测试性能表达式在每次渲染时重新求值复杂计算应该被缓存computed!-- ❌ 不推荐模板中写复杂逻辑 -- div{{ userList.filter(u u.age 18).map(u u.name).join(, ) }}/div !-- ✅ 推荐逻辑放在计算属性中 -- script setup import { computed } from vue const adultNames computed(() userList.value.filter(u u.age 18).map(u u.name).join(, ) ) /script template div{{ adultNames }}/div /template六、指令系统概览Vue 的指令Directives是带有v-前缀的特殊 HTML 属性。指令的职责是当其绑定的表达式的值发生变化时响应式地对 DOM 应用特定的行为。6.1 指令的完整语法结构v-指令名 :参数 .修饰符 表达式组成部分说明示例指令名指令的核心标识v-on、v-bind、v-for参数可选用冒号:指定v-bind:href→ 参数是href修饰符可选用点.指定用来改变指令行为v-on:click.stop→.stop修饰符阻止事件冒泡表达式部分指令可省略指令的值v-ifisVisible→ 表达式是isVisible6.2 核心指令速查表指令简写用途本篇涉及v-bind:动态绑定 HTML 属性✅ 已讲v-on绑定事件监听器第 6 篇v-model无双向绑定表单输入第 6 篇v-if/v-else-if/v-else无条件渲染第 5 篇v-show无基于 CSS 的显示/隐藏第 5 篇v-for无列表渲染第 5 篇v-html无渲染原始 HTML✅ 已讲v-text无设置元素的 textContent较少用{{ }}更常用本系列不单独展开v-once无只渲染一次后续不更新第 32 篇性能优化v-memo无缓存渲染结果Vue 3.2第 32 篇v-pre无跳过编译输出原始内容极少使用v-cloak无编译前隐藏未编译的模板CDN 场景极少使用本系列后续安排v-on和v-model将在第 6 篇详解v-if/v-show/v-for将在第 5 篇详解。6.3 动态参数Vue 3 新特性指令的参数可以是动态的用方括号[]包裹script setup import { ref } from vue const attributeName ref(href) const url ref(https://vuejs.org) const eventName ref(click) /script template !-- 动态绑定属性名 -- a :[attributeName]urlVue 官网/a !-- 等价于 :hrefurl -- !-- 动态事件名 -- button [eventName]handleClick点击/button !-- 等价于 clickhandleClick -- /template动态参数的注意事项动态参数表达式最终必须返回一个字符串null表示移除该绑定避免在动态参数中使用非字符串如对象、数字等Vue 会发出警告动态参数中不能使用空格和引号如:[key Value]是合法的但:[key value]不合法6.4 指令的优先级深入理解当一个元素上同时存在多个指令时Vue 按照以下优先级顺序处理v-pre v-once v-for v-if/v-else-if/v-else v-on v-bind v-model v-show实际影响v-for的优先级高于v-if如果在同一元素上同时使用v-if会在每次迭代中重复执行性能浪费。最佳实践避免在同一元素上同时使用v-for和v-if应使用计算属性过滤数据或将v-if放在外层容器上。v-on的优先级高于v-bind这意味着click可以覆盖:click绑定虽然基本不会这么做。七、综合示例一个完整的组件模板让我们综合运用本篇所学的所有语法编写一个相对完整的组件模板script setup langts import { ref, computed } from vue // 响应式数据 const user ref({ name: 王小明, age: 28, email: wangxmexample.com, isActive: true }) const theme ref(dark) const customClass ref(highlight) const userAvatar ref(https://example.com/avatar.jpg) const isExternal ref(true) const dynamicEventName ref(click) // 计算属性 const userDisplayName computed(() ${user.value.name}${user.value.age}岁 ) // 方法 function getUserCardStyle() { return { backgroundColor: theme.value dark ? #1a1a2e : #f5f5f5, color: theme.value dark ? #ffffff : #333333, padding: 20px, borderRadius: 12px } } function handleAction() { console.log(点击了按钮) } /script template div :class[user-card, { active: user.isActive, highlight: customClass }] :stylegetUserCardStyle() v-bind{data-user-id: user.id || new} !-- 文本插值 表达式 -- h2{{ userDisplayName }}/h2 !-- 属性绑定 -- img :srcuserAvatar :alt${user.name}的头像 classavatar / !-- 原始 HTML仅在内容可信时使用 -- div v-htmlspan classbadge${user.isActive ? 在线 : 离线}/span/div !-- 动态属性 -- a :hrefmailto:${user.email} :targetisExternal ? _blank : _self {{ user.email }} /a !-- 动态参数高级 -- button :[dynamicEventName]handleAction classaction-btn 操作 /button /div /template style scoped .user-card { transition: all 0.3s; } .user-card.active { border-left: 4px solid #4caf50; } .user-card.highlight { box-shadow: 0 4px 12px rgba(0,0,0,0.15); } .avatar { width: 60px; height: 60px; border-radius: 50%; } .badge { background: #4caf50; color: white; padding: 2px 8px; border-radius: 12px; } /style八、本篇小结知识点核心内容关键注意点文本插值{{ expression }}只能写表达式不能写语句自动转义 HTML原始 HTMLv-htmlexpression⚠️ 有 XSS 风险仅用于可信内容属性绑定v-bind:attrexpr简写:attr布尔属性用null/undefined移除空字符串是真值class 绑定对象语法:class{ active: isActive }和数组语法最常用的动态样式绑定方式style 绑定对象语法:style{ color: colorVar }自动处理浏览器前缀动态参数:[dynamicKey]value表达式必须返回字符串指令系统v-*前缀包含参数和修饰符优先级需了解避免在v-for上同时用v-if九、思考与练习基础练习在上一节创建的项目中修改App.vue的模板创建一个个人信息卡片。要求使用ref定义姓名、年龄、职业、头像 URL使用插值显示姓名和年龄年龄后加岁使用v-bind绑定头像src使用:class绑定一个名为card的样式类使其居中、带阴影陷阱排查分析以下模板代码指出所有潜在问题并给出修正方案template div p{{ let username admin }}/p p{{ userInfo }}/p div v-htmluserInput/div input :disabled / div v-foritem in list v-ifitem.isActive {{ item.name }} /div /div /template动态参数实验在 Vue 项目中尝试用动态参数绑定一个属性名观察渲染结果。思考如果动态参数的值是null会发生什么深度思考为什么 Vue 模板中的表达式不能访问window对象这种设计带来的利弊分别是什么下篇预告第 4 篇我们将深入 Vue 的响应式系统——这是 Vue 最核心、最精髓的部分。你将学到ref和reactive的本质区别和选型建议为什么需要用.value访问ref的值模板中自动解包的规则响应式数据在 JavaScript 中如何触发视图更新准备好揭开 Vue 响应式的神秘面纱