编译
把模板编译成 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
对象