generate函数分析

203 阅读2分钟

generate函数主要如下:

第一步,调用CodegenState函数,根据options初始化一个state

dataGenFns: (2) [ƒ, ƒ]
directives: {on: ƒ, bind: ƒ, cloak: ƒ, model: ƒ, text: ƒ, }
maybeComponent: ƒ (el)
onceId: 0
options: {outputSourceRange: true, shouldDecodeNewlines: false, shouldDecodeNewlinesForHref: false, delimiters: undefined, comments: undefined, }
pre: false
staticRenderFns: []
transforms: []
warn: ƒ (msg, range, tip)

第二步,调用genElement函数,传递ast和state,返回code

genElement函数如下:

第一步先判断当前节点是否有父节点,如果有设置父节点

之后就是根据节点属性处理不同的情况,源码部分如下:

if (el.staticRoot && !el.staticProcessed) { //静态根节点
    return genStatic(el, state) 
  } else if (el.once && !el.onceProcessed) {    //v-once指令 只做一次渲染
    return genOnce(el, state)
  } else if (el.for && !el.forProcessed) {  //v-for指令
    return genFor(el, state)
  } else if (el.if && !el.ifProcessed) {    //v-if
    return genIf(el, state)
  } else if (el.tag === 'template' && !el.slotTarget && !state.pre) {   //处理标签是template的情况
    return genChildren(el, state) || 'void 0'   
  } else if (el.tag === 'slot') {   //处理slot插槽
    return genSlot(el, state)
  } else {  //剩下就是组件或者元素
    // component or element
    let code
    if (el.component) {
      code = genComponent(el.component, el, state)
    } else {
      let data
      if (!el.plain || (el.pre && state.maybeComponent(el))) {
        data = genData(el, state)
      }
​
      const children = el.inlineTemplate ? null : genChildren(el, state, true)
      code = `_c('${el.tag}'${
        data ? `,${data}` : '' // data
      }${
        children ? `,${children}` : '' // children
      })`
    }
    // module transforms
    for (let i = 0; i < state.transforms.length; i++) {
      code = state.transforms[i](el, code)
    }
    return code
  }

genStatic函数作用如下:

第一步,现在state.staticRenderFns放入genElement生成的静态render

第二步,返回_m(staticRenderFns最后一个元素),也就是调用staticRenderFns里面的静态渲染函数

genOnce函数作用如下:

先判断是否含有v-if指令,有就执行genIf函数

如果当前节点被包裹在v-for指令,并且当前节点是静态节点的情况下(v-once),那么就获取v-for指令的key,生成 _o(_c(tag, data, children), number, key)

如果都不符合以上的情况,那么就是一个简单的静态节点,和处理静态节点操作一样。调用genStatic函数,得到_m(xxx)

genFor函数作用如下:

第一步,先获取

exp = el.for,

alias = el.alias,

iterator1 = el.iterator1

iterator2 = el.iterator2。

下面按照模板进行说明

<div id="content" >
    <span v-for="(item,index,arr) in 3" :key="index">
        {{item}}
    </span>
</div>

el.for指的是3

el.alias指的是item

el.iterator1指的是,index

el.iterator2指的是,arr

类似forEach或者其他的数组函数一样,这三个都是里面的参数

第二步就是拼接了

`${altHelper || '_l'}((${exp}),` +
    `function(${alias}${iterator1}${iterator2}){` +
    `return ${(altGen || genElement)(el, state)}` +
'})'

结果

`_l((3),function(item,index,arr){return _c('span',{key:index},[_v("\n        "+_s(item)+"\n      ")])})`

genIf函数如下:

里面调用genIfConditions方法

genIfConditions方法接受四个参数:

conditions : el.ifConditions 结构如下:

<!--template-->
<div id="content">
    <span v-if="c">
        1
    </span>
    <span v-else-if="d">2</span>
    <span v-else>3</span>
</div><!--conditions -->
[
    0: {
        block: {type: 1, tag: 'span', attrsList: Array(0), attrsMap: {…}, rawAttrsMap: {…}, …}
        exp: "c"
    },
    1: {
        block: {type: 1, tag: 'span', attrsList: Array(0), attrsMap: {…}, rawAttrsMap: {…}, …}
        exp: "d"
    },
    2: {
        block: {type: 1, tag: 'span', attrsList: Array(0), attrsMap: {…}, rawAttrsMap: {…}, …}
        exp: undefined
    }
]

state :

dataGenFns: (2) [ƒ, ƒ]
directives: {on: ƒ, bind: ƒ, cloak: ƒ, model: ƒ, text: ƒ, }
maybeComponent: ƒ (el)
onceId: 0
options: {outputSourceRange: true, shouldDecodeNewlines: false, shouldDecodeNewlinesForHref: false, delimiters: undefined, comments: undefined, }
pre: false
staticRenderFns: []
transforms: []
warn: ƒ (msg, range, tip)

altGen : 函数

altEmpty : 函数

第一步先判断conditions是否为空,如果为空就返回一个_e()

第二步先从conditions获取第一项,并且移除。防止递归时死循环。之后获取exp属性,也就是判断条件

第三步将block参数传给genTernaryExp函数,genTernaryExp根据block调用genElement函数生成渲染函数字符串

第四步递归调用genIfConditions,传递conditions, state, altGen, altEmpty

生成结果如下:

<!--template-->
<div id="content">
    <span v-if="c">
        1
    </span>
    <span v-else-if="d">2</span>
    <span v-else>3</span>
</div><!--render-->
`(c)?_c('span',[_v("\n        1\n      ")]):(d)?_c('span',[_v("2")]):_c('span',[_v("3")])`

genChildren函数如下:

第一步,先去获取子节点,判断子节点是否不为0

第二步,优化,如果子节点只有一个并且上面有v-for指令,并且子节点不是template或者slot,那么就调用genElement方法

第三步,循环子节点,用gen函数生成render函数(gen函数根据节点类型调用不同得函数,如果节点类型是1就调用genElement,如果节点类型是3就调用genComment,如果都不是就调用genText)

<!--template-->
<template>
    <div>1</div>
    <div>2</div>
    <div>3</div>
</template><!--render-->
`[_c('div',[_v("1")]),_v(" "),_c('div',[_v("2")]),_v(" "),_c('div',[_v("3")])]`

genSlot函数如下:

第一步,先获取slotName,如果没有就是default

第二步,先调用genChildren函数,生成render函数

第三步,生成字符串函数

//template
<slot>
    <div>123</div>
</slot>
​
//render str
`_t(${slotName}${children ? `,function(){return ${children}}` : ''}`
​
//render res
`_t("default",function(){return [_c('div',[_v("123")])]}`

第四步,获取attrs属性,也就是在slot上面绑定的属性

第五步,将atts的值追加到res上面,然后返回res

genComponent函数如下:

第一步,调用genChildren函数,生成渲染函数字符串

<!--template-->
<a is="blog">
    <div>123</div>
</a><!--render-->
`[_c('div',[_v("123")])]`

第二步,调用genData函数,返回渲染函数字符串

`_c(${componentName},${genData(el, state)}${
    children ? `,${children}` : ''
})`
​
`_c("blog",{tag:"a"},[_c('div',[_v("123")])])`

genData函数如下:

let data = '{'// directives first.
// directives may mutate the el's other properties before they are generated.
const dirs = genDirectives(el, state)
if (dirs) data += dirs + ','// key
if (el.key) {
    data += `key:${el.key},`
}
// ref
if (el.ref) {
    data += `ref:${el.ref},`
}
if (el.refInFor) {
    data += `refInFor:true,`
}
// pre
if (el.pre) {
    data += `pre:true,`
}
// record original tag name for components using "is" attribute
if (el.component) {
    data += `tag:"${el.tag}",`
}
// module data generation functions
for (let i = 0; i < state.dataGenFns.length; i++) {
    data += state.dataGenFns[i](el)
}
// attributes
if (el.attrs) {
    data += `attrs:${genProps(el.attrs)},`
}
// DOM props
if (el.props) {
    data += `domProps:${genProps(el.props)},`
}
// event handlers
if (el.events) {
    data += `${genHandlers(el.events, false)},`
}
if (el.nativeEvents) {
    data += `${genHandlers(el.nativeEvents, true)},`
}
// slot target
// only for non-scoped slots
if (el.slotTarget && !el.slotScope) {
    data += `slot:${el.slotTarget},`
}
// scoped slots
if (el.scopedSlots) {
    data += `${genScopedSlots(el, el.scopedSlots, state)},`
}
// component v-model
if (el.model) {
    data += `model:{value:${
        el.model.value
    },callback:${
        el.model.callback
    },expression:${
        el.model.expression
    }},`
}
// inline-template
if (el.inlineTemplate) {
    const inlineTemplate = genInlineTemplate(el, state)
    if (inlineTemplate) {
        data += `${inlineTemplate},`
    }
}
data = data.replace(/,$/, '') + '}'
// v-bind dynamic argument wrap
// v-bind with dynamic arguments must be applied using the same v-bind object
// merge helper so that class/style/mustUseProp attrs are handled correctly.
if (el.dynamicAttrs) {
    data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})`
}
// v-bind data wrap
if (el.wrapData) {
    data = el.wrapData(data)
}
// v-on data wrap
if (el.wrapListeners) {
    data = el.wrapListeners(data)
}
return data

genDirectives函数主要用来处理节点的指令

genProps函数主要用来处于props

genScopedSlots函数主要用来处于作用域插槽

genInlineTemplate主要用来处于template

genHandlers主要用来处于事件

总之来说就是拼接属性和节点和事件

到了最后的部分了 也是最重要的部分 这部分是主要生成渲染函数字符串的地方,先回到genElement函数里面内

首先先定义了个data,之后调用genData函数,将值赋给data

之后就是处理子节点,用genChildren方法

最后返回已经处理好的渲染函数字符串