[Vue源码分析] 模板的编译
最近小组有个关于vue源码分析的分享会,提前准备一下…前言:Vue有两个版本:Runtime + Compiler 、 Runtime only ,前者是包含编译代码的版本,后者不包含编译代码,编译过程需要借助webpack的vue-loader,接下来分析的是Runtime + Compiler版本,编译过程感觉挺复杂的,所以下边只是大概分析一下整个流程,源码理解直接写在源码中。### 模板的编
最近小组有个关于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
的函数,该函数返回的是一个对象,包括 compile
和compileToFunctions
, compileToFunctions
对应的就是$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方法的定义位于src/shared/util.js中,如下:
其实extend只是一个循环,把之前解析出来的属性循环出来并挂载到传入的ast对象上。(更新于2019.02.25 )
更多指令的解析有兴趣可以自行研究一下~~
更多推荐
所有评论(0)