编译
把模板编译成 render 函数的过程称作编译。
生成render函数在$mount方法里

进入compileToFunctions
1 | function createCompileToFunctionFn (compile) { |
重要的compile方法,传入template和options

进入compile方法
1 | function createCompilerCreator (baseCompile) { |
这一行:
1 | var compiled = baseCompile(template.trim(), finalOptions); |
进入baseCompile方法
1 | var createCompiler = createCompilerCreator(function baseCompile ( |
1)var ast = parse(template.trim(), options);解析模板字符串生成 AST
2)const code = generate(ast, options)生成代码

关于ast的生成就没看了,后面再看吧
看一下generate方法
1 | function generate ( |
用genElement方法生成code,再用字符串包起来,这里看起来跟node执行某个模块文件的方式有点类似啊
进入genElement方法
1 | function genElement (el, state) { |
判断当前 AST 元素节点的属性执行不同的代码生成函数。
最后生成的code是这样的
1 | _c('p',{on:{"click":function($event){return changeUserList()}}},[_v("\n "+_s(name)+"\n ")])" |
它里面用到的方法调用在这里
1 | function installRenderHelpers (target) { |
调用renderMixin的时候,加在Vue的prototype上

生成ast过程
看一个简单的例子:
main.js
1 | import Vue from 'vue' |
从parseHTML方法开始
1 | function parseHTML (html, options) { |
上面那个例子在这个阶段发生了这些事情
1)进入while循环后,匹配第一个div开始标签,然后html变为图片中这样,之后返回match(match中有标签名,属性数组,开始结束位置等)

2)往stack数组push这个对象,并且把lastTag的名称赋值为div

3)然后进入start方法,在这个方法里创建ASTElement,然后为这个对象添加属性
1 | start: function start (tag, attrs, unary, start$1, end) { |

之后push到另外一个存ast对象的stack里面
4)第二次循环,在advance(text.length)的地方把html字符串前面的空白字符截取掉

之后赋值给html字符串,之后开始又一轮循环

5)匹配p标签的时候有点不一样,因为p标签有指令v-if,所以match对象的attrs属性是个数组

6)接着往stack里push这个match对象,并且把lastTag的名称赋值为p

7)然后进入start方法,在这个方法里为p创建ASTElement,然后为这个对象添加属性

之后push到另外一个存ast对象的stack里面
8)又一次循环,这次是插值表达式开头

9)进入chars方法,这里创建了一个child对象,push到了children数组里
1 | chars: function chars (text, start, end) { |

10)下一次循环,html字符串剩2个结束标签

匹配到结束标签

进入parseEndTag方法之后进入end方法,拿到ast的stack栈中最后一个元素,修改currentParent的值。
1 | end: function end (tag, start, end$1) { |
在closeElement方法里,stack.length -= 1;减少ast的stack数组长度,之后建立元素父子关系关联
在这里改变非ast的stack的数组长度

11)又一次循环,之后返回生成好的ast对象
