// `createCompilerCreator` allows creating compilers that use alternative // parser/optimizer/codegen, e.g the SSR optimizing compiler. // Here we just export a default compiler using the default parts. var createCompiler = createCompilerCreator(function baseCompile( template, options ) { var ast = parse(template.trim(), options); if (options.optimize !== false) { } var code = generate(ast, options); return { ast: ast, render: code.render, staticRenderFns: code.staticRenderFns } });
可以看到 baseCompile 的代码非常的简短主要核心代码。
最终 baseCompile 的返回值
{ ast: ast, render: code.render, staticRenderFns: code.staticRenderFns }
最终返回了抽象语法树( ast ),渲染函数( render ),静态渲染函数( staticRenderFns ),且render 的值为code.render ,staticRenderFns 的值为code.staticRenderFns ,也就是说通过 generate 处理 ast 之后得到的返回值 code 是一个对象 ...
接下来让我们把目光聚焦在 Vue 的 parser,它是如何将字符串模板解析为抽象语法树(AST
)的。
var ast = parse(template.trim(), options);
在讲解parse之前你需要对编译过程以及其中的技术点有个宏观、概要的了解。
引用自维基百科:
它主要的目的是将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻译为计算机能解读、运行的低阶机器语言的程序。源代码一般为高阶语言(High-level language),如Pascal、C、C++、C# 、Java等,而目标语言则是汇编语言或目标机器的目标代码(Object code)。
编译器的技术分为词法分析、语法分析和语义分析三个部分,通常编译器的第一项工作叫做词法分析。就像阅读文章一样,文章是由一个个的中文单词组成的。程序处理也一样,只不过这里不叫单词,而是叫做“词法记号”,英文叫 Token。
<div id="app" v-if="ret">{{ message }}</div>
编译器会识别出 div a span 这些标签,id class style v-if v-for 这样的属性、指令,还有花括号符号这样的插值操作...等。这些都是 Token。
那么,如何写一个程序来识别 Token 呢?
其实,我们可以手写程序制定一些规则来区分每个不同的 Token,这些规则用“正则文法”表达,符合正则文法的表达式称为“正则表达式”。通过他们来完成具体的词法分析工作。
编译器下一个阶段的工作是语法分析。词法分析是识别一个个的单词,而语法分析就是在词法分析的基础上识别出程序的语法结构。这个结构是一个树状结构,是计算机容易理解和执行的。
程序也要定义良好的语法结构,它的语法分析过程,就是构造这么一棵树。一个程序就是一棵树,这棵树叫做抽象语法树(Abstract Syntax Tree,AST)。树的每个节点(子树)是一个语法单元,这个单元的构成规则就叫“语法”。
而我们这里要讲的 parser 就是在编译器对源代码处理的第一步,parser 把某种特定格式的文本(字符串)转换成某种数据结构的程序(对象),并且这个数据结构是编译器能够理解的,因为编译器的后续步骤,比如上面提到的 句法分析,类型检查/推导,代码优化,代码生成 等等都依赖于该数据结构。
注:parse & parser 这两个单词,不要混淆,parse 是动词,代表“解析”的过程,parser 是名词,代表“解析器”。
Vue 的编译器也不例外, 在词法分析阶段 Vue 会把字符串模板解析成一个个的令牌(token),该令牌将用于句法分析阶段,在句法分析阶段会根据令牌生成一棵 AST,最后再根据该 AST生成最终的渲染函数,这样就完成了代码的生成。
var ast = parse(template.trim(), options);
回归到刚刚的代码,已知 parse 函数就是用来解析模板字符串的,最终生成AST。
function parse(template, options) { // 省略... parseHTML(template, { warn, expectHTML: options.expectHTML, isUnaryTag: options.isUnaryTag, canBeLeftOpenTag: options.canBeLeftOpenTag, shouldDecodeNewlines: options.shouldDecodeNewlines, shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref, shouldKeepComment: options.comments, start (tag, attrs, unary) { // 省略... }, end () { // 省略... }, chars (text: string) { // 省略... }, comment (text: string) { // 省略... } }) return root }
需要注意到在parse 函数内部主要通过调用parseHTML 函数对模板字符串进行解析,实际上parseHTML 函数的作用就是用来做词法分析的,而parse函数的作用则是在词法分析的基础上做句法分析从而生成一棵AST。本节我们主要分析一下Vue是如何对模板字符串进行词法分析的,也就是parseHTML 函数的实现。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
长按识别二维码并关注微信
更方便到期提醒、手机管理