3.1 模板语法与编译过程 本节摘要:模板语法是 Vue 的表层界面——插值、指令缩写、修饰符、动态参数。本节一边过语法,一边把每条语法翻译成它编译后的产物,让你透过字符串看到渲染函数,最终解释模板的所有限制(单根元素、表达式而非语句)从何而来。 学习目标 阅读完本节,你应当能够: 熟练使用插值、属性绑定、事件缩写与常用修饰符; 说出编译三阶段各自的输入输出,并看懂一段简化渲染函数; 解释"模板只能有一个根元素(Vue 2)"与"只能写表达式"的限制来源; 区分编译时能确定的内容与必须留到运行时的内容。 表层语法速览 最常见的几类写法先立住(Vue 2 与 Vue 3 一致): 修饰符值得多说一句,它把"处理事件的样板代码"变成了声明。
本节摘要:模板语法是 Vue 的表层界面——插值、指令缩写、修饰符、动态参数。本节一边过语法,一边把每条语法翻译成它编译后的产物,让你透过字符串看到渲染函数,最终解释模板的所有限制(单根元素、表达式而非语句)从何而来。
阅读完本节,你应当能够:
最常见的几类写法先立住(Vue 2 与 Vue 3 一致):
<div id="app"> <!-- 插值:文本 --> <p>{{ message }}</p> <!-- 属性绑定与事件绑定的完整式与缩写式 --> <img v-bind:src="imgUrl" :alt="imgName"> <button v-on:click="greet" @click="greet">问好</button> <!-- 修饰符:声明式的事件行为 --> <form @submit.prevent="onSubmit">防止默认提交</form> <input @keyup.enter="search" placeholder="回车搜索"> <!-- 动态参数:参数本身来自数据 --> <a @[eventName]="doSomething">动态事件名</a> </div>
修饰符值得多说一句,它把"处理事件的样板代码"变成了声明。.prevent 等价于在回调里手写取消默认行为,.enter 等价于判断按键码,.once 等价于触发后解绑。事件处理器越薄,组件越好测。
模板字符串在构建阶段(脚手架项目里)或运行时(CDN 引入且传了 template 选项)被加工,流水线三步:
第一步,解析。 解析器逐字符扫描模板,遇到标签生成元素节点、遇到插值生成表达式节点、遇到纯文本生成文本节点,最终得到一棵抽象语法树(AST),每个节点记录标签名、属性、子节点、以及关键的 static 标记位。
第二步,优化。 遍历 AST 找出不含变量、不含指令的子树,标记为静态。这一步是为第 4 章的 diff 服务的:静态子树在新旧比对时可以整树跳过。
第三步,生成。 把 AST 串接成一段函数代码字符串,类似:
// 模板 <p class="tip">{{ message }}</p> 的简化产物 function render() { return createElement('p', { class: 'tip' }, [ createTextVNode(this.message) ]); }
注意产物里 message 仍是一个属性访问——它必须留到运行时求值,因为值随时会变;而 class 这个静态属性已经在编译期写死。编译时能定的定死,运行时才变的保留,这条原则贯穿 Vue 3 的一切编译优化。

为什么只能写表达式不能写语句? 插值 {{ }} 里的内容会被拼进渲染函数作为一个表达式求值,{{ if (ok) { return '是' } }} 拼出来是语法错误的函数体。想要分支,用三元表达式或计算属性:
<!-- 错误:语句 --> {{ if (ok) { ... } }} <!-- 正确:表达式 --> {{ ok ? '是' : '否' }}
为什么 Vue 2 模板只能有一个根元素? 渲染函数必须返回单个根 vnode,多个根元素意味着返回值无法成型。Vue 3 放宽为多根(片段),本质是运行时 vnode 结构升级后顺手解除的限制——又一例"表层语法的限制其实来自底层产物"。
为什么 CDN 模式功能全但体积大? 完整构建把运行时编译器也打进了包(模板字符串在浏览器里现场编译);脚手架项目在构建期就把模板编译成了渲染函数,线上只需要更小的运行时版本。能用预编译就预编译,这是老生常谈却常被忽视的体积优化。
工程里的模板通常写在单文件组件的 template 块里,构建工具在打包前完成编译。也正因为编译发生在构建期,Vue 3 才敢做更激进的优化——静态节点提升(把永不变化的 vnode 提到渲染函数外,只创建一次)与补丁标志(编译时给动态节点打标,更新时只比对有标记的部分)。这些第 10 章细说,此处记住因果:优化发生在编译器里,所以只有预编译的项目享受得到。
模板如何变成渲染函数清楚了,下一节把全部常用指令放进这条流水线里过一遍。