parse方法生成ast之后,又调用了generate方法将ast转化为render函数
// 生成渲染函数字符串
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
generate(ast, options)
generate主要调用了genElement方法,生成render字符串
export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
const state = new CodegenState(options)
const code = ast ? genElement(ast, state) : '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
genElement(ast, state)
export function genElement (el: ASTElement,
state: CodegenState): string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if (el.staticRoot && !el.staticProcessed) {
// 如果是一个静态的树, 如 <div id="app">123</div>
// 生成_m()方法
// 静态的渲染函数被保存至staticRenderFns属性中
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
// v-once 转化为_o()方法
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
// _l()
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(el, state)
genStatic会将ast转化为_m()方法
例如模版是纯静态dom
<div id="app">
<p>测试</p>
</div>
转换后的render函数如下

render函数是一个_m()方法,真正的静态render函数在staticRenderFns属性中
genOnce(el, state)
如果v-once在v-for中,那么就会生成_o()方法, 否则将其视为静态节点
例如template如下
v-once不在v-for中,以静态节点处理
<div id="app">
<p v-once>{{test}}</p>
</div>
转换后如下

v-once在v-for中
<div id="app">
<div :key="index" v-for="(item,index) in array">
<p v-once>{{item}}</p>
</div>
</div>
转换后如下

genFor(el, state)
v-for会转换为_l()
不再赘述示例
genIf(el, state)
genIf会将v-if转换为表达式,示例如下
转换后如下

genComponent(el.component, el, state)
如果是组件 或者 是标签元素的话, 那么会被转化为_c()
示例如下

<div id="app">
<comp1></comp1>
</div>
转换后如下所示
总结
generate方法内部逻辑还是很复杂的,但仅做了一件事情,就是将ast转化为render函数的字符串,形成一个嵌套结构的方法,模版编译生成的_c(),_m(),_l等等其实都是生成vnode的方法,在执行vue.$mount方法的时候,会调用vm._update(vm._render(), hydrating)方法,此时_render()中方法会执行生成的render()函数,执行后会生成vnode,也就是虚拟dom节点
以下是生成vnode相关的方法,后续讲解
export function installRenderHelpers (target: any) {
//
target._o = markOnce
target._n = toNumber
// 返回字符串
target._s = toString
target._l = renderList
target._t = renderSlot
target._q = looseEqual
target._i = looseIndexOf
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
// 创建一个文本的vnode
target._v = createTextVNode
target._e = createEmptyVNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
target._d = bindDynamicKeys
target._p = prependModifier
}