Skip to content

解析模板时候如何保证 AST 层级关系

Vue 在 HTML 解析器parseHTML的开头定义了一个栈stack,这个栈的作用就是用来维护 AST 节点层级的

通过前文我们知道,HTML 解析器在从前向后解析模板字符串时,每当遇到开始标签时就会调用start钩子函数,那么在start钩子函数内部我们可以将解析得到的开始标签推入栈中,而每当遇到结束标签时就会调用end钩子函数,那么我们也可以在end钩子函数内部将解析得到的结束标签所对应的开始标签从栈中弹出。请看如下例子:

html
<p>
    <div>
        <span></span>
    </div>
</p>

当解析到开始标签<p>时,就把 p 压入栈中,然后继续解析,当解析到<div>时,再把 div 压入栈中,同理,再把 span 压入栈中,当解析到结束标签</span>时,此时栈顶的标签刚好是 span 的开始标签,那么就用 span 的开始标签和结束标签构建 AST 节点,并且从栈中把 span 的开始标签弹出,那么此时栈中的栈顶标签 div 就是构建好的 span 的 AST 节点的父节点,如下图: parseHTMLStack.png

这样我们就找到了当前被构建节点的父节点。这只是栈的一个用途,它还有另外一个用途,我们再看如下模板字符串:

html
<p><div><span></div></p>

按照上面的流程解析这个模板字符串时,当解析到结束标签</div>时,此时栈顶的标签应该是 </span> 才对,而现在是 </div>,那么就说明 span 标签没有被正确闭合,此时控制台就会抛出警告:tag has no matching end tag.相信这个警告你一定不会陌生。这就是栈的第二个用途: 检测模板字符串中是否有未正确闭合的标签。