主题
解析模板时候如何保证 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 节点的父节点,如下图:
这样我们就找到了当前被构建节点的父节点。这只是栈的一个用途,它还有另外一个用途,我们再看如下模板字符串:
html
<p><div><span></div></p>
按照上面的流程解析这个模板字符串时,当解析到结束标签</div>
时,此时栈顶的标签应该是 </span>
才对,而现在是 </div>
,那么就说明 span 标签没有被正确闭合,此时控制台就会抛出警告:tag has no matching end tag.
相信这个警告你一定不会陌生。这就是栈的第二个用途: 检测模板字符串中是否有未正确闭合的标签。