Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue.js源码阅读 #16

Open
HuazhuLi opened this issue Mar 26, 2017 · 2 comments
Open

Vue.js源码阅读 #16

HuazhuLi opened this issue Mar 26, 2017 · 2 comments

Comments

@HuazhuLi
Copy link
Owner

No description provided.

@HuazhuLi HuazhuLi reopened this Jun 16, 2017
@HuazhuLi HuazhuLi changed the title 15 Vue.js源码阅读 Jul 27, 2017
@HuazhuLi
Copy link
Owner Author

在大部分时间里,我们使用vue.js,几乎必然都是在使用vue的模版功能;
在初学vue的时候,每当编译报错时,笔者第一反应就是:没按照官方文档写(这是DSL (Domain-Specific Language, 领域特定语言)带来的一些问题);

vue 的模版简单易用,但是由于有些开发者不能完全知晓内部的处理逻辑,会显得有点“黑魔法”,
所以笔者特意去阅读了vue的源码,来了解vue到底为我们开发者提供了何种便利,能让我们舒服自然地进行声明式编程;

在开始阅读compile这部分的源码之前,首先我们想知道的是:vue的compile部分,在vue实例化过程中,起着怎样的作用?
其实,在vue里面写render function,或者说使用jsx写法的时候,是不需要经过compile这部分的。
compile代码,只做了一件事:把template转成render function。

在vue实例创建完成以后(created后),首先会检测有没有el选项,有的话就把对应的outerHTML取出来,
并且当作template来处理;如果没有,就检测有没有template选项;
完成以上操作以后,开始了compile这一阶段。
(使用render function 和 template的区别,也是使用独立构建和运行时构建的区别。这在后面会提及)
放一张Vue的生命周期图:

在上图,我们可以清晰地看到:compile发生在created和mounted的时间点之前,那问题来了,在这个阶段,一个Vue实例到底经历了什么?
或者说,template要转成render function,需要哪些步骤?

(首先,我们必须先确定正在讨论的问题,因为vue2以及vue1的compile阶段,差别较大;所以在本文,我们只讨论vue2的compile阶段)

compile部分文件结构以及对应作用如下:

compile
|——parser (template -> ast)
|——index.js(入口文件)
|——text-parser.js(对ast里面的ASTTEXT & ASTEXPRESSION进行判断以及处理)
|——html-parser.js (对template里面的dom树进行遍历操作)
|——filter-parser.js (对template的filter进行解析)
|——entity-decoder.js
|——codegen (optimized ast -> render function)
|——events.js
|——index.js
|——directives (对特殊指令的一些处理)
|——bind.js
|——index.js
|——model.js
|——on.js
|——creatCompiler.js(创造compile实例)
|——error-detector.js
|——helpers.js(公用函数库)
|——optimizer.js(ast -> optimized ast)
|——index.js(入口文件)
|——to-function.js

在compile这个过程里,框架主要做了三件事:
1.parse(解释template,使其转成ast)
2.optimize(优化ast)
3.code generate(把ast转成render function)

结束这三个过程以后,我们得到了render function(渲染函数);render function会渲染出VNODE(也就是virtual dom的节点)
(至于template为什么要被编译成render function,以及vue为何支持jsx,我们在稍后会提及相应的内容,我们先把精力放在compile这部分的源码上)

附上一张compile/index.js里的compile函数调用图:

(在vue.js 2.4里,作者在部分函数里使用typescript,以上的typescript其实很好懂:typescript需要对函数的参数,以及函数的返回值,定义类型;
在以上函数,函数的参数 template/options 分别是string类型和compileroptions类型;
返回值类型,是compiledresult。)

简而言之,creatCompilerCreator函数接受baseCompile函数为参数,而后者又调用了parse/optimize函数/generate函数;
最后把值返回给creatCompiler。

这一流程简单的图示如下:

解说版:
v-text指令冒险记

v-text指令经过parse里面的html parser,htmlparser读到了

&

,于是调用了parser里的start和end方法,这两个方法,生成了
一个astelement,这个astelement里面有directive这个属性,里面有v-text这个指令作为属性名以及对应的值;

然后在经过optimize这一阶段,这个astelement被标记成static(静态的)

最后经过codegen,这个astelement被输出成staticrenderfn(静态渲染函数)

然后这个static render function会渲染出一个虚拟dom节点,也就是VNODE这种数据结构,最后virtual dom tree会和之前的virtual dom tree进行diff,把diff的结果patch到实际的dom节点上,完成了下图过程。

@HuazhuLi
Copy link
Owner Author

parser,也叫解释器,在vue.js里,parser所做的事情很简单,就是把模版里层层嵌套的dom结构,转换成一个parent属性为空,而有children属性的的ASTElement,这个ASTElement,vue里边把它命名为根(root)。

(ast,也就是我们常说的,抽象语法树,后文提到的ASTElement,ASTExpression,ASTText,都是抽象语法树上面的节点,上文提到的root,就是一个ASTElement。)

由于parser在最后,只会返回一个ASTElement,如上文提到的那样,这个ASTElement叫做root,它的parent属性为空,只有child属性。
(所以在这里,我们知道了,为什么vue.js需要我们在template模版最外层,必须安排一个根节点;假设你不这么干,你在template里面写了div1 div2 div3节点,然后vue会怎么做呢?他会遍历div1的dom结构,并且返回div1这个标签对应的ast作为root,最后直接跳过div2 div3;)
todo:这段有待商榷

过程简介

就如我们第一章讲的那样:
parse这个过程,就是把template转成ast的过程,这个过程并非一蹴而就,而是分为好几个子过程:

为了说明这个过程,笔者在此以下面的模版作为例子:

对于这个模版,parser做了以下的几步:

第一步:parser调用html parser(一个开源的html解释器,作者是John Resig),并且给html parser传入四个函数,分别start() end() chars() comment();
这个解释器原理比较简单,解析的主要过程是:

这个parser在于遇到开始标签的时候,举个例子:

,他就会调用一个对应的函数start(),这个函数(vue作者定制)会生成一个ASTElement,记录节点 tagName, attributes 等信息;
这个parser在遇到结束标签的时候,举个例子

,他就会调用对应的函数方法end();
这个parser在遇到开始标签和结束标签之间的内容时候,就会调用一个方法char();
在遇到注释时,会调用对应的comment()函数;

了解这四个函数,是理解其生成的三种ast节点的关键;

ASTNode

上文提及到的四个函数,直接生成了三种ast节点,这三种ast类型如下:

-ASTElement
-ASTText
-ASTExpression

对应地看一下 Vue 2.0 源码中 AST 数据结构 的定义:
img

注意:为了避免文章过长,我在以上的代码中注释了 ASTElement 中的许多属性,点击上方 AST 数据结构的链接可查看完整代码。

img

ASTExpression & ASTText

function name & function involved

ASTElement & VUE指令编译优先级

只要在调用char()方法的时候,vuejs才会生成astelement以及astexpression,否则都是生成astelement
(图示)

2.convert node to ast
对as

理解ast的三种类型
以及对应的html代码

编译的优先级
每一个指令对应的parse过程

为什么会出现编译优先级的问题

区分更改代码/重新销毁/在使用中我们只更改数据

说说function调用判断逻辑

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant