Vue 的编译模块包含 4 个目录:
compiler-core
compiler-dom // 浏览器compiler-sfc // 单文件组件compiler-ssr // 服务端渲染
其中 compiler-core 模块是 Vue 编译的核心模块,并且是平台无关的。而剩下的三个都是在 compiler-core 的基础上针对不同的平台作了适配处理。
Vue 的编译分为三个阶段,分别是:parse、transform、codegen。
其中 parse 阶段将模板字符串转化为语法抽象树 AST。transform 阶段则是对 AST 进行了一些转换处理。codegen 阶段根据 AST 生成对应的 render 函数字符串。
Parse
Vue 在解析模板字符串时,可分为两种情况:以 < 开头的字符串和不以 < 开头的字符串。
不以 < 开头的字符串有两种情况:它是文本节点或 {{ exp }} 插值表达式。
而以 < 开头的字符串又分为以下几种情况:元素开始标签
元素结束标签
注释节点
文档声明
用伪代码表示,大概过程如下:
while (s.length) {
if (startsWith(s, '{{')) {
// 如果以 '{{' 开头 node = parseInterpolation(context, mode)
} else if (s[0] === '
// 以 < 标签开头 if (s[1] === '!') {
if (startsWith(s, '
AST 节点
所有的 AST 节点定义都在 compiler-core/ast.ts 文件中,下面是一个元素节点的定义:
export interface BaseElementNode extends Node {
type: NodeTypes.ELEMENT // 类型 ns: Namespace // 命名空间 默认为 HTML,即 0 tag: string // 标签名 tagType: ElementTypes // 元素类型 isSelfClosing: boolean // 是否是自闭合标签 例如
props: Array // props 属性,包含 HTML 属性和指令 children: TemplateChildNode[] // 字节点}
一些简单的要点已经讲完了,下面我们再从一个比较复杂的例子来详细讲解一下 parse 的处理过程。
{{ test }}
一个文本节点
上面的模板字符串假设为 s,第一个字符 s[0] 是 < 开头,那说明它只能是刚才所说的四种情况之一。
这时需要再看一下 s[1] 的字符是什么:如果是 !,则调用字符串原生方法 startsWith() 看看是以 '
{{ test }}
一个文本节点