今天看啥  ›  专栏  ›  派大星的黑板报

Vue源码之compile之generate

派大星的黑板报  · 掘金  ·  · 2021-02-24 17:43
阅读 5

Vue源码之compile之generate

generate

generate 函数⾸先实例化CodegenState然后通过 genElement(ast, state) ⽣成 code ,再把 code ⽤ with(this){return ${code}}} 包裹起来放到render中。

CodegenState

options 是传入的一些判断函数或者指令函数,CodegenState初始化实例的编译状态,因为这个函数是给实例初始化一些属性的,看到很明显就是给实例添加上了很多属性,this.xxxx 什么的。

  • dataGenFns
  • directives

这也是个数组,存放的是 Vue 自有指令的独属处理函数,包括以下几个指令的处理函数
v-on:绑定事件
v-bind:绑定属性
v-cloak:编译前隐藏DOM
v-model:双向绑定
v-text:插入文本
v-html:插入html
当你在模板中使用到以上的指令的时候,Vue 会调用相应的函数先进行处理

  • staticRenderFns

一个数组,用来存放静态根节点的render 函数,每个实例都独有这个属性,如果没有静态根节点就为空。

genElement

可以看到基本就是递归判断当前 AST 元素节点的属性执⾏不同的代码⽣成函数。不要尝试去一下子看完全部,应该根据场景对应去阅读才是正解。

genStatic 对于静态节点的处理

template

ast

code

流程

总结

1.首先genElement判断el节点是否存在staticRoot标识,并且一开始staticProcessed为false,如果满足这两个条件就调用genStatic函数。

2.genStatic首先把staticProcessed设为true,其次往staticRenderFns丢入genElement返回结果,最后render中code的返回值为_m(staticRenderFns.length - 1)。

3.再次genElement时staticProcessed为true就往下执行,执行genData中的属性的解析,然后进行genChildren,遍历children执行genNode,如果子节点类型为1说明是元素节点继续执行genElement,如果不是元素节点也不是注释节点那么就调用genText,判断是否为表达式返回对应的编码。

4.最终遍历结束后就得到了最终的编码,对于静态节点这段编码存放在staticRenderFns中。

genOnce 对v-once的处理

template

ast

code

流程

1.首先执行genElement外层标签不存在标识直接执行到genData然后执行genChildren,然后执行genCode,因为为元素节点所以继续执行genElement。

2.这时el存在once 标识,并且一开始onceProcessed为false,调用genOnce函数。

3.genOnce首先把onceProcessed 设为true,如果没有el.if && !el.ifProcessed和el.staticInFor条件不满足执行genStatic(el, state)。

4.genStatic首先把staticProcessed设为true,其次往staticRenderFns丢入genElement返回结果,最后render中children的返回值为_m(staticRenderFns.length - 1)。

5.再次genElement时staticProcessed为true就往下执行,执行genData解析,然后进行genChildren,遍历children执行genNode,如果子节点类型为1说明是元素节点继续执行genElement,如果不是元素节点也不是注释节点那么就调用genText,判断是否为表达式返回对应的编码。

6.最终遍历结束后就得到了最终的编码,对于静态节点这段编码存放在staticRenderFns中。

genFor

template

ast

code

流程

1.首先执行genElement外层标签不存在标识直接执行到genData然后执行genChildren,因为存在el.for,所以执行genElement。

2.这时el存在for标识,并且一开始forProcessed为false,调用genFor函数。

3.genFor首先获取到el.for,el.alias和el.iterator1,然后执行一些报错其中有没写Key的报错,把forProcessed设为true,然后返回了for的编码_l…然后执行genElement。

5.再次genElement时staticProcessed为true就往下执行,存在el.key执行genData对key的解析,然后进行genChildren,遍历children执行genNode,如果子节点类型为1说明是元素节点继续执行genElement,如果不是元素节点也不是注释节点那么就调用genText,判断是否为表达式返回对应的编码。

6.最终遍历结束后就得到了最终的编码。

genIf

template

ast

code

流程

1.首先执行genElement外层标签不存在标识直接执行到genData然后执行genChildren,因为存在el.for,所以执行genElement。

2.这时el存在if标识,并且一开始ifProcessed为false,调用genIf函数。

3.genIf首先把ifProcessed设为true,然后执行genIfConditions函数传入el.ifConditions.slice()。

4.如果没有condition直接返回'_e()',否则从conditions.shift()推出一位,如果condition存在表达式,返回三元表达式,条件?genTernaryExp -> 也就是genElement解析节点 : genIfConditions 再次执行genIfConditions
如果不存在表达式直接调用genTernaryExp去解析节点。

5.最终遍历结束后就得到了最终的编码。

class和style

template

ast

code

流程

1.首先执行genElement外层标签不存在标识直接执行到genData然后执行genChildren,因为也不存在标识直接执行到genData。

2.此时genData对el做dataGenFns处理,dataGenFns是平台用来处理class和style的,最终返回对象。

3.处理完data后执行genChildren,遍历children执行genNode,如果子节点类型为1说明是元素节点继续执行genElement,如果不是元素节点也不是注释节点那么就调用genText,判断是否为表达式返回对应的编码。

总结

通过对一些指令的解析,我们对从 ast -> code 这⼀步有了⼀些了解,编译后⽣成的代码就是在render时执⾏的代码。由于 genCode 的内容有很多,大概思路都是差不多,无非就是根据不同的节点属性去走不同的流程生成不同的结果,最后拼接起来的一个字符串。至于slot和事件我们后面再单独说,又是烧脑的一天。加油打工人。




原文地址:访问原文地址
快照地址: 访问文章快照