3.1 模板语法与编译过程


文档摘要

3.1 模板语法与编译过程 本节摘要:模板语法是 Vue 的表层界面——插值、指令缩写、修饰符、动态参数。本节一边过语法,一边把每条语法翻译成它编译后的产物,让你透过字符串看到渲染函数,最终解释模板的所有限制(单根元素、表达式而非语句)从何而来。 学习目标 阅读完本节,你应当能够: 熟练使用插值、属性绑定、事件缩写与常用修饰符; 说出编译三阶段各自的输入输出,并看懂一段简化渲染函数; 解释"模板只能有一个根元素(Vue 2)"与"只能写表达式"的限制来源; 区分编译时能确定的内容与必须留到运行时的内容。 表层语法速览 最常见的几类写法先立住(Vue 2 与 Vue 3 一致): 修饰符值得多说一句,它把"处理事件的样板代码"变成了声明。

3.1 模板语法与编译过程

本节摘要:模板语法是 Vue 的表层界面——插值、指令缩写、修饰符、动态参数。本节一边过语法,一边把每条语法翻译成它编译后的产物,让你透过字符串看到渲染函数,最终解释模板的所有限制(单根元素、表达式而非语句)从何而来。

学习目标

阅读完本节,你应当能够:

  1. 熟练使用插值、属性绑定、事件缩写与常用修饰符;
  2. 说出编译三阶段各自的输入输出,并看懂一段简化渲染函数;
  3. 解释"模板只能有一个根元素(Vue 2)"与"只能写表达式"的限制来源;
  4. 区分编译时能确定的内容与必须留到运行时的内容。

表层语法速览

最常见的几类写法先立住(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 章细说,此处记住因果:优化发生在编译器里,所以只有预编译的项目享受得到

本节要点回顾

  • 语法即语法糖:缩写、修饰符、动态参数最终都翻译为渲染函数里的普通函数调用与配置;
  • 三阶段:解析成 AST、优化打静态标记、生成渲染函数,静态标记直接服务后续 diff 跳过;
  • 限制的来源:单表达式约束来自函数体拼接,Vue 2 单根约束来自 vnode 返回值结构;
  • 预编译的价值:体积更小的运行时 + 解锁静态提升等编译期优化,CDN 现场编译两头都亏;
  • 看产物学语法:拿不准某写法的成本时,看它编译成什么,比背性能清单可靠。

模板如何变成渲染函数清楚了,下一节把全部常用指令放进这条流水线里过一遍。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U