In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
小编给大家分享一下Vue3中AST解析器的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
1、生成 AST 抽象语法树
首先我们来重温一下 baseCompile 函数中有关 ast 的逻辑及后续的使用:
export function baseCompile( template: string | RootNode, options: CompilerOptions = {}): CodegenResult { /* 忽略之前逻辑 */ const ast = isString(template) ? baseParse(template, options) : template transform( ast, {/* 忽略参数 */} ) return generate( ast, extend({}, options, { prefixIdentifiers }) )}
因为我已经将咱们不需要关注的逻辑注释处理,所以现在看函数体内的逻辑会非常清晰:
生成 ast 对象
将 ast 对象作为参数传入 transform 函数,对 ast 节点进行转换操作
将 ast 对象作为参数传入 generate 函数,返回编译结果
这里我们主要关注 ast 的生成。可以看到 ast 的生成有一个三目运算符的判断,如果传进来的 template 模板参数是一个字符串,那么则调用 baseParse 解析模板字符串,否则直接将 template 作为 ast 对象。baseParse 里做了什么事情才能生成 ast 呢?一起来看一下源码,
export function baseParse( content: string, options: ParserOptions = {}): RootNode { const context = createParserContext(content, options) // 创建解析的上下文对象 const start = getCursor(context) // 生成记录解析过程的游标信息 return createRoot( // 生成并返回 root 根节点 parseChildren(context, TextModes.DATA, []), // 解析子节点,作为 root 根节点的 children 属性 getSelection(context, start) )}
在 baseParse 的函数中我添加了注释,方便大家理解各个函数的作用,首先会创建解析的上下文,之后根据上下文获取游标信息,由于还未进行解析,所以游标中的 column、line、offset 属性对应的都是 template 的起始位置。之后就是创建根节点并返回根节点,至此ast 树生成,解析完成。
2、创建 AST 的根节点export function createRoot( children: TemplateChildNode[], loc = locStub): RootNode { return { type: NodeTypes.ROOT, children, helpers: [], components: [], directives: [], hoists: [], imports: [], cached: 0, temps: 0, codegenNode: undefined, loc }}
看 createRoot 函数的代码,我们能发现该函数就是返回了一个 RootNode 类型的根节点对象,其中我们传入的 children 参数会被作为根节点的 children 参数。这里非常好理解,按树型数据结构来想象就可以。所以生成 ast 的关键点就会聚焦到 parseChildren 这个函数上来。parseChildren 函数如果不去看它的源码,见文之意也可以大致了解这是一个解析子节点的函数。接下来我们就来一起来看一下 AST 解析中最关键的 parseChildren 函数,还是老规矩,为了帮助大家理解,我会精简函数体内的逻辑。
3、解析子节点function parseChildren( context: ParserContext, mode: TextModes, ancestors: ElementNode[]): TemplateChildNode[] { const parent = last(ancestors) // 获取当前节点的父节点 const ns = parent ? parent.ns : Namespaces.HTML const nodes: TemplateChildNode[] = [] // 存储解析后的节点 // 当标签未闭合时,解析对应节点 while (!isEnd(context, mode, ancestors)) {/* 忽略逻辑 */} // 处理空白字符,提高输出效率 let removedWhitespace = false if (mode !== TextModes.RAWTEXT && mode !== TextModes.RCDATA) {/* 忽略逻辑 */} // 移除空白字符,返回解析后的节点数组 return removedWhitespace ? nodes.filter(Boolean) : nodes}
从上文代码中,可以知道 parseChildren 函数接收三个参数,context:解析器上下文,mode:文本数据类型,ancestors:祖先节点数组。而函数的执行中会首先从祖先节点中获取当前节点的父节点,确定命名空间,以及创建一个空数组,用来储存解析后的节点。之后会有一个 while 循环,判断是否到达了标签的关闭位置,如果不是需要关闭的标签,则在循环体内对源模板字符串进行分类解析。之后会有一段处理空白字符的逻辑,处理完成后返回解析好的 nodes 数组。在大家对于 parseChildren 的执行流程有一个初步理解之后,我们一起来看一下函数的核心,while 循环内的逻辑。
在 while 中解析器会判断文本数据的类型,只有当 TextModes 为 DATA 或 RCDATA 时会继续往下解析。
第一种情况就是判断是否需要解析 Vue 模板语法中的 "Mustache"语法 (双大括号) ,如果当前上下文中没有 v-pre 指令来跳过表达式,并且源模板字符串是以我们指定的分隔符开头的(此时 context.options.delimiters 中是双大括号),就会进行双大括号的解析。这里就可以发现,如果当你有特殊需求,不希望使用双大括号作为表达式插值,那么你只需要在编译前改变选项中的 delimiters 属性即可。
接下来会判断,如果第一个字符是 "
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.