vue源码解读--codegen

目录导航

本节代码如下



辅助函数映射表

vue源码解读--codegen_第1张图片

经过上一节分析我们知道,vue对ast树进行了一次标记,标记的结果就是br和div被标记为staticRoot。这一节,我们尝试分析ast=>code的过程。执行generate

vue源码解读--codegen_第2张图片

    实例化CodegenState,并将配置对象options缓存到私有属性option上,state则保存当前实例

    若ast树不存在则返回一个"_c('div')",_c其实就是createElement函数。其实这里我们可以预想下code的形式,它和我们手写render是一样的:{tag,options,children}。显然当前我们的ast是存在的,故调用genElement传入ast和state

vue源码解读--codegen_第3张图片

            框红的位置,拿到的是我们的根ast节点即main,它的parent不存在,故跳过。对于其子节点ul、br、div而言则进入逻辑。由于v-pre有保留代码串的特性,故对于这样的标签及子标签是不需要去解析生成code的

            对于我们当前的示例,会先走向框蓝的位置。此时的ast节点是ul。el.ifProcessed则会在当前次处理后被标记为true,有效避免递归死循环。调用genIf

vue源码解读--codegen_第4张图片

                        将el.ifProcessed置为true,由于genElement是一次递归的过程,这将避免程序无出口造成 死循环

                        调用genIfConditions,在创建ast阶段,如果存在if指令,则会将其进行解析,并向数组push一位成员,该成员由当前的ast节点和对应的值两个key组成。故入参为:[{block:ul节点,exp:isList},{block:p,exp:undefined}]、undefined、undefined

vue源码解读--codegen_第5张图片

                          如果conditions中无值,则调用_e()创建一个文本节点。我们这里有,则跳过。

                          使用shift拿到第一个成员,即{block:ul,exp:isList},进入if判断。并生成一段字符串返回:`isList?genElement函数返回值:genIfConditions调用`,经过下一步则实际为`isList?genElement函数返回值:genElement函数返回值`

                                    --genIfConditions的入参为[{block:p,exp:undefined}\,在执行一二步,conditions跳过,拿到{block:p,exp:undefined},由于exp为undefined,故本次走else逻辑,返回字符串:`genElement函数`

(故,这里会重复执行两次genElement函数,一次是ul,一次是p)

ul


    由于已经将el.ifProcessed标记为true,故本次将走向elde逻辑

vue源码解读--codegen_第6张图片

                 显然ul下有子节点,故执行genChildren

vue源码解读--codegen_第7张图片

`                        拿到li节点,进入判断,由于li上有v-for,故进入到框红的位置

                          由于li不是一个组件,故normalizationType=",0"

                          此时生成的字符串为:`genElement返回值,0`,再次进入genElement函数,当解析完li后则实际为`_c("li",{key:"i"},_v(_s(v))),0`

li——_c("li",{key:"i"},_v(_s(v)))

    由于li标签上存在v-for,故本次将调用genFor

vue源码解读--codegen_第8张图片

                对于v-for指令而言,vue会要求我们提供key值,如果不存在则warn用户

                返回一个字符串:`_l("list",function(v,i){return genElement返回值})`,再次调用genElement

,由于已经将el.forProcessed置为true,故将走向else逻辑,调用gendata

vue源码解读--codegen_第9张图片

                  gendata将对标签存在的属性做一些处理,当前我们的li标签只有一个key还未被处理,故简化后的gendata如下

vue源码解读--codegen_第10张图片

                    返回`{key:i}`

                    返回到genElement函数,data值为`{key:i}`。向下调用genchildren处理li内的插值,将返回`_v(_s(v)),_v(_s(v)),0`

                    故code为`_c("li",{key:"i"},_v(_s(v)))`

li标签内的插值——`_v(_s(v)),_v(_s(v)),0`

    本次不满足for指令,故走else

vue源码解读--codegen_第11张图片

                    --调用getNormalizationType将children拍平,我们这里不需要,故为0

                    --调用genNode,由于当前是一个表达式节点,type=2,故实际上调用的是genText

vue源码解读--codegen_第12张图片

            返回`_v(_s(v))`,故真正返回的字符串是'_v(_s(v)),_v(_s(v)),0'



一步一步走太过于繁琐,但是经过以上对ul和li以及li的子元素的分析,我们已经可以总结。codegen的过程是深度遍历的过程,它将对我们不同的ast节点类型调用不同的预先定义好的函数,每一个标签都对应_c函数,当标签存在不同的属性时它将会把他们解析成对象形式并作为_c的第二个参数。由此由内向外不断嵌套。并最终利用with进行包裹使用new Function将字符串转换成可执行的代码。其最终的基本像是将和我们手写render函数是一样的,如

createElement('main',{

    staticClass:"app"

},[

    createElement('ul',{},[createElement('li')]),

    createElement('br'),

    createElement('div')

])

只不过,vue将createElement替换成了_c,将ul、br、div合成了_l

你可能感兴趣的:(vue源码解读--codegen)