Categories
程式開發

Vue生成AST算法的解析


在看vue源码的过程中,不只加深了对vue本身的理解,也理解了正则,以及各种设计模式。

博客大部分是从代码开始讲起,所以,我打算更细致的讲一讲这部分的思想。

首先我们要知道什么是AST,以及为什么要用AST生成虚拟dom。

AST是指抽象语法树(abstract syntax tree),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式。Vue在mount过程中,template会被编译成AST语法树。

简单来说,在js中AST的表现形式是像下面的结构

Vue生成AST算法的解析 1

它是怎么来的呢? 是通过js中的类html字符串转换而来的,所以我们的任务就变成了把类html转换成这种结构啦。 下面我先给出一串模板

const template = `


`

#1

首先我们对这个字符串进行trim操作,去除两端的空格。然后我们要用 let textStart = html.indexOf(‘<')判断这个字符串<的位置。这时候就分成了三个部分

Vue生成AST算法的解析 2

我们从最简单的textStart = 0的情况开始

1.1

这时候又会有两种可能性

Vue生成AST算法的解析 3

这一部分的结构讲完了, 再继续看看<0的情况

1.2

当textStart<0的时候,只有一种可能,这段html字符串已经没有标签或者注释了,即是纯文本。

1.3

当textStart > 0的时候,说明在标签或者注释之前含有文本(这里我们暂时不考虑文本中有<的情况,大家可以自己去尝试一下)

2

知道了所有的情况,我们来了解一下怎么处理这些情况。

2.1

对1.的情况,当匹配到标签的时候,比如

我们要把它的tagName, id, class等等属性得到。设置两个栈。一个叫checkStack,用于与之后匹配到的结束标签进行比对。一个叫AstStack,用于辅助AST的建立。

Vue生成AST算法的解析 4

Vue生成AST算法的解析 5

checkStack中单纯存放了tagName和attr,而AstStack中存放的是他的父元素和子元素,这些可以用于建立一个AST。

所以流程如下:

匹配到开始标签 –> 获得各种属性,创建一个match对象–> 推入checkStack中–>推入AstStack中 (这里先不讨论自闭和标签的情况,等会会用代码来详细讨论)

如果匹配到注释,不推入checkStack,具体流程如下:

匹配到注释开始 –> 获得注释所有的内容,并匹配到注释结束标签 –> 不推入checkStack –> 从AstStack中获取最后一个元素, 把该注释对象推入AstStack栈顶的children数组中

2.2

没有标签或注释,则会把该段纯文本推入AstStack栈顶元素的children中, 并不再遍历该段html,如果没有栈顶元素,则会报错

2.3

与2.2情况类似,只是还会继续遍历html

3

知道了流程和怎么处理,我们可以愉快的开始看代码啦!我们来看看精简版的,然后大家熟悉了流程可以再去看vue的源码

先来定义几个正则

const attribute = /^s*([^s"'\/=]+)(?:s*(=)s*(?:"([^"]*)"+|'([^']*)'+|([^s"'=`]+)))?/
const ncname = '[a-zA-Z_][\w\-\.]*'
const qnameCapture = `((?:${ncname}\:)?${ncname})`
// 匹配开始标签开始部分
const startTagOpen = new RegExp(`^/
// 匹配结束标签
const endTag = new RegExp(`^]*>`)
// 匹配注释
const comment = /^')
console.log(commentEnd)
if (commentEnd >= 0) {
if (opt.comment) {
opt.comment(html.substring(4, commentEnd)) // 保存注释