[Vue源码分析] 模板的编译

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/Fabulous1111/article/details/84557294

最近小组有个关于vue源码分析的分享会,提前准备一下…
前言:
Vue有两个版本:Runtime + Compiler 、 Runtime only ,前者是包含编译代码的版本,后者不包含编译代码,编译过程需要借助webpack的vue-loader,接下来分析的是Runtime + Compiler版本,编译过程感觉挺复杂的,所以下边只是大概分析一下整个流程,源码理解直接写在源码中。

模板的编译

之前分析Virtual DOM的时候我们分析过模板到真实 DOM 渲染的过程,中间有一个环节把模板编译成render函数,这个过程称作编译。

(1)编译入口追踪

编译入口在分析virtual dom的时候已经提及过,位于src/platforms/web/entry-runtime-with-compiler.js下的$mount方法(红框部分):
在这里插入图片描述
可以看到compileToFunctions方法将模板编译成了render以及staticRenderFns函数,通过对象的解构赋值获取结果并赋值给options

compileToFunctions在 src/platforms/web/compiler/index.js中定义,如下:
在这里插入图片描述
发现这是一个赋值的过程,值由createCompiler产生,createCompiler方法在 src/compiler/index.js 中定义,如下:
在这里插入图片描述
这个方法是createCompilerCreator的返回值,createCompilerCreator中传入的参数是一个方法,在此暂时不看参数方法里边的内容,因为这里只是一个调用,并没有执行什么。先看看createCompilerCreator的定义,它的定义在src/compiler/create-compiler.js中,如下:
在这里插入图片描述
createCompilerCreator返回一个 createCompiler 的函数,该函数返回的是一个对象,包括 compilecompileToFunctionscompileToFunctions对应的就是$mount方法中调用的compileToFunctions 方法,它是 createCompileToFunctionFn 方法的返回值,我们接下来看一下 createCompileToFunctionFn 方法,它的定义在 src/compiler/to-function/js 中,这个函数便是compileToFunctions的最终定义,如下:
在这里插入图片描述
compileToFunctions中编译的核心是compile的调用,compile是通过参数的方式传入的,也就是createCompilerCreator中定义的compile,现在我们返回去看compile是什么,之前的图折叠了,现在展开如下:
在这里插入图片描述
compile前部分代码都是在处理配置参数,实际上的编译过程只有代码中红框部分,也就是调用baseCompile方法,这个方法时调用createCompilerCreator时通过参数的方式传入的,也就是之前介绍到的当时说暂时不用看的代码,重新上一次图:
在这里插入图片描述
编译入口追踪到这里告一段落(vue项目支持多个平台,不同平台配置不一样,所以入口绕了很多个圈)。可以看到最终主要步骤有三步,一步是通过parse生成ast树,一步是optimize,看英文意思及传入参数应该是对ast树进行优化的一个过程,一步是调用generate生成code,接下来看看这几个步骤都干了什么。

(2) parse

编译过程首先就是对模板做解析,生成 AST语法树,我们可以在parse后debugger一下,看看AST语法树的模样。

新建一个vue demo,在main.js做如下配置:
在这里插入图片描述
然后再源码中parse后打个断点,或者打印一下:
在这里插入图片描述
可以看到,控制台输入了以下内容,这便是ast的结构:
在这里插入图片描述
至此ast的结构我们了解了,但ast是怎么生成的呢?接下来我们看看parse是什么。parse的定义位于src/compiler/parser/index.js中。
这个过程很复杂,概括地说就是把template模板字符串转换成AST树,它是一种用JavaScript对象的形式来描述整个模板。整个parse的过程是利用正则表达式顺序解析模板,当解析到开始标签、闭合标签、文本的时候都会分别执行对应的回调函数,最终达到构造AST树的目的。
这块内容很多,挑几个点讲一下:

①:解析标签

解析HTML是通过调用parseHML方法完成的,它的定义位于src/compiler/parser/html-parser
调用:
在这里插入图片描述
定义:
在这里插入图片描述
这个方法也是比较复杂,整体来说就是循环解析template ,用顶部预先定义好的一堆正则表达式做正则匹配,处理开始标签和结束标签,对于不同情况分别进行不同的处理,直到解析完毕。

比较关键的一个点事在匹配的过程中会利用 advance 函数不断前进整个模板字符串,直到字符串末尾。
在这里插入图片描述

举个例子:

假如模板本来是这个样子的,可以理解为一个队列,目前队列的索引为0:
在这里插入图片描述
通过调用advance(4)后,通过html.substring(4),队列的索引就变成了4,当前待解析模板就变成了如下:
在这里插入图片描述

②:解析文本、表达式

除了处理开始标签和结束标签,还需要处理文本,通过parseText实现,源码位于src/compiler/parser/text-parsre.js
在这里插入图片描述
回头看看之前打印出来的ast,可以看到打上标记的表达式:
在这里插入图片描述

③:解析指令,以v-for为例

v-for指令解析的入口是processFor方法,该方法定义位于src/compiler/parser/index.js,此方法依赖parseFor以及extend方法,共同完成v-for的解析。
在这里插入图片描述
大概思路:通过正则匹配v-for,匹配到了就调用parseFor方法,parseFor方法位于同文件中:
在这里插入图片描述
这个方法也是通过正则匹配,分别匹配出不同的内容,比如’v-for="(item, index) in data"’,匹配出来的res.for是data,res.alias是item,res.iterator是index,随后返回解析出来的 结果,传给processFor中的res常量res,接着调用extend方法完成解析,extend方法的定义位于/Users/xiaoqiang/Desktop/GitHub/vue/src/shared/util.js中,如下:
在这里插入图片描述
其实extend只是一个循环,把之前解析出来的属性循环出来并挂载到传入的ast对象上。

猜你喜欢

转载自blog.csdn.net/Fabulous1111/article/details/84557294