# Generate
- Vue的编译器将组件的
<template>的html解析成 AST 对象 - 从 AST 生成运行渲染函数,即
render(),包括staticRenderFns数组, 里面存放了所有静态节点的渲染函数
# createCompilerCreator
- 就是处理为使用
render(h) {}属性写出的Vnode
export function createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: ComplierOptions
): CompiledResult {
// 将模板解析为 AST, 每个
const ast = parse(template.trim(), options)
// 优化,遍历AST,为每个节点做静态标记
// 标记每个节点是否为静态节点,然后进一步标记处静态根节点 在后续更新中可以跳过静态节点
if (options.optimize !== false) {
optimize(ast, options)
}
// 代码生成, 将 ast 转换成可执行的 render 函数的字符串形式
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
# generate
export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
// 实例化 CodegenState 对象
const state = new CodegenState(options)
// 生成字符串格式的代码,比如 _c(tag, data, children, normalizationType)
// 类似 render 的 h(tag, data, children, ...)
// 不只是_c 如果是函数式组价 结果为_m(0)
const code = ast ? genElement(ast, state) : '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
# genElemenet
- 处理元素上的各种
v-if、v-for...
export function genElement (el: ASTElement, state: CodegenState): string {
// 处理 v-pre, v-pre 指令会跳过当前元素的处理
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if (el.staticRoot && !el.staticProcessed) {
/**
* 当前节点为静态根节点且未被 genStatic 处理过
* 1. 将当前静态节点的渲染函数放到 staticRenderFns 数组中
* 2. 返回一个可执行函数 _m(idx, true or '')
*/
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
/**
* 当前节点带有 v-once 指令的节点
* 1. 当前节点存在 v-if 指令,得到一个三元表达式, condition ? render1 : render2
* 2. 当前节点是一个包含在 v-for 指令内部的静态节点,得到`_o(_c(tag, data, children), number, key)`
* 3. 单纯的一个 v-once 节点。 得到`_m(idsx true or '')`
*/
return genOnce(el, state)
} else if (el.if && !el.ifProcessed) {
// 处理带有 v-if 的节点
return genIf(el, state)
} else if (el.for && el.forProcessed) {
// 返回 `_l(exp, funciton(alias, iterator1, iterator2){return _c(tag,data,children)})`
return genFor(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
// 当前节点是 template 但却不是插槽的 使用 template 做 v-if 或者 v-for的
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
// 插槽 `_t(slotName, children, attrs, bind)`
return genSlot(el, state) // 6. 插槽里
} else {
// 处理动态组件和普通元素
let code
if (el.component) {
// 处理动态组件,得到`_c(compName, data, children)`
code = genComponent(el.component, el, state)
} else {
// 自定义组件和原生标签
let data
if (!el.plain || (el.pre && state.maybeComponent(el))) {
// 返回 JSON 类型的 data: { key: a, ref: b, props: c, ... }
data = genData(el, state)
}
// 处理子节点,得到多有子节点字符串格式的代码组成的数组
// [_c(tag, data, children), ...]
const children = el.inlineTemplate ? null : genChildren(el, state, true)
code = `_c('${el.tag}'${data ? `,${data}` : ''}
${children ? `,${children}` : ''})`
}
// 如果提供了 transformCode 方法
for (let i = 0; i < state.transforms.length; i++) {
code = state.transforms[i](el, code)
}
return code
}
}
# genChildren
export function genChildren (
el: ASTElement,
state: CodegenState,
checkSkip?: boolean,
altGenElement?: Function,
altGenNode?: Function
): string | void {
const children = el.children
if (children.length) {
const el = children[0]
// 省略代码 优化
const normalizationType = checkSkip
? getNormalizationType(children, state.maybeComponent)
: 0
const gen = altGenNode || genNode
// 返回 children 数组
// `[_c(tag, data, children, normalizationType), ....]`
// 递归 通过 genElement 处理 children
return `[${children.map(c => gen(c, state)).join(',')}]${normalizationType ? `,${normalizationType}` : ''}`
}
}
# genNode
function genNode(node: ASTNode, state: CodegenState): string {
// 递归 genElement
if (node.type === 1) {
return genElement(node, state)
} else if (node.type === 3 && node.isComment) {
// `_e(${JSON.string(comment.text)})`
return genComment(node)
} else {
// `_v(${text.type === 2 ? text.expression : transformSpecialNewlines(JSON.stringify(text.text))})`
return genText(node)
}
}
# genData
- 处理自定义组件,常态标签上的属性 组成 JSON。 类似
{ key: a, ref: b, attrs: {}, ... }
export function genData(el: ASTElement, state: CodegenState): string {
let data = '{'
// 首先处理指令, 因为指令可能在生成其他属性之前改变这些属性
// 返回 directives: [{ name, rawname, value, arg, modifiers }, ...]
const dirs = genDirectives(el, state)
if (dirs) data += dirs + ','
if (el.key) data += `key:${el.key},`
if (el.ref) data += `ref:${el.ref},`
if (el.refInFor) data += `refInFor: true,`
if (el.pre) data += `pre: true,`
// 动态组件
if (el.component) data += `tag: "${el.tag}",`
// 获得节点的 staiticClass、class、staticStyle、style
for (let i = 0; i < state.dataGenFns.length; i++) {
data += state.dataGenFns[i](el)
}
// 其他属性
if (el.attrs) data += `attrs:${genProps(el.attrs)},`
// domProps
if (el.props) data += `domProps: ${genProps(el.props)},`
// 自定义事件
if (el.events) data += `${genHandlers(el.events, false)},`
// 处理 .native 修饰符事件
if (el.nativeEvents) data += `${genHandlers(el.nativeEvents, true)},`
// 非作用域插槽
if (el.slotTarget && !el.slotScope) data += `slot:${el.slotTarget},`
// 作用域插槽 在6 插槽里 生成 _u() 函数
if (el.scopedSlots) data += `${genScopedSlots(el, el.scopedSlots, state)},`
if (el.model) data += `model:{value:${el.model.value},callback:${el.model.callback},expression:${el.model.expression}},`
if (el.inlineTemplate) {
const inlineTemplate = genInlineTemplate(el, state)
if (inlineTemplate) data += `${inlineTemplate},`
}
data = data.replace(/,$/, '') + '}'
if (el.dynamicAttrs) {
// 存在动态属性
data = `_b(${data}),"${el.tag}",${genProps(el.dynamicAttrs)}`
}
if (el.wrapData) {
data = el.wrapData(data)
}
if (el.wrapListeners) {
data = el.wrapListenters(data)
}
return data
}
# genDirectives
- 处理 指令:
directives:[{ name: '', value: '', ... }]
function genDirectives(el: ASTElement, state: CodegenState): string | void {
// 获取指令数组
const dirs = el.directives
if (!dirs) return
let res = 'directives:['
// 标记,用于标记指令是否需要在运行时完成的任务, 比如 v-model 的 input 事件
let hasRuntime = false
let i, l, dir, needRuntime
for (i = 0, l = dirs.length; i < l; i++) {
dir = dirs[i]
needRuntime = true
// 获取当前指令的处理方法 v-html...
const gen: DirectiveFunction = state.directives[dir.name]
if (gen) {
// 指令编译方法, 如果需要运行时完成一部分任务 则返回 true
needRuntime == !!gen(el, dir, state.warn)
}
if (needRuntime) {
hasRuntime = true
res += `{name:"${dir.name}",rawName:"${dir.rawName}"
${dir.value ?`,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''}
${dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ''}
${dir.modifiers ? `${,modifiers:${JSON.sttingify(dir.modifiers)}}` : ''}
},`
}
}
if (hasRuntime) {
// 只有存在运行时任务 才会返回 res
return res.sloce(0, -1) + ']'
}
}
# genProps
function genProps(props: Array<ASTAttr>): string {
// 静态属性
let staticProps = ``
// 动态属性
let dynamicProps = ``
// 遍历属性数组
for (let i = 0; i< props.length; i++) {
const porp = props[i]
const value = __WEEX__
? generateValue(prop.value)
: transformSpeciaNewlines(prop.value)
if (prop.dynamic) {
dynamicProps += `${prop.name},${value},`
} else {
staticProps += `"${prop.name}":${value}`
}
// 去掉静态属性最后的逗号
staticProps = `{${staticProps.slice(0, -1)}}`
if (dynamicProps) {
// 如果存在动态属性 则返回 _d(静态属性字符串,动态属性字符串)
return `_d(${staticProps},[${dynamicProps.slice(0, -1)}])`
} else {
// JSON 格式的对象
return staticProps
}
}
}
# genHandlers
- 处理方法的
{ on: { ... }, nativeOn: { ... } }
export function genHandlers (
events: ASTElementHandlers,
isNative: boolean
): string {
const prefix = isNative ? 'nativeOn' : 'on:'
let staticHandlers = ``
let dynamicHandlers = ``
for (const name in events) {
// 获取回调函数的函数名, 即 this.methodName 或者 [this.methodName1, ...]
const handlerCode = genHandler(events[name])
if (events[name] && events[name].dynamic) {
dynamicHandlers += `${name},${handlerCode}`
} else {
// 静态
staticHandlers += `"${name}":${handlerCode},`
}
}
staticHandlers = `{staticHanlders.slice(0, -1)}`
if (dynamicHandlers) {
return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`
} else {
return prefix + staticHandlers
}
}
# genStatic
- 使用
<template>标签生成的会做的优化,如果是自己写的render(h) {}是没有这种优化的 - renderStatic
// 生成静态节点的渲染函数,将当前静态节点的渲染函数放到 staticRenderFns 数组中
function genStatic(el: ASTElement, state: CodegenState): string {
// 标记当前静态节点已经被处理过了
el.staticProcessed = true
const originalPreState = state.pre
if (el.pre) {
state.pre = el.pre
}
// 将静态根节点的渲染函数 push 到 staticRenderFns 数组中
state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`)
state.pre = originalPreState
// 返回一个可执行函数:_m(idx, true or '')
// idx = 当前静态节点的渲染函数在 staticRenderFns 数组中的下标
return `_m(${state.staticRenderFns.length - 1}${el.staticInFor} ? ',true' : '')`
}
# genOnce
function genOnce(el, state) {
// 标记当前节点已经被处理过
el.onceProcessed = true
if (el.if && !el.ifProcessed) {
// 带有 v-if 指令但是没有被 genIf 处理过
return genIf(el, state)
} else if (el.staticInFor) {
let key = ''
let parent = el.parent
while (parent) {
if (parent.for) {
key = parent.key
break
}
parent = parent.parent
}
// key 不存在则给出提示, v-once 只能用于带有 key 的 v-for 节点内部
if (!key) {
warn()
return genElement(el, state)
}
// 生成 `_o(_c(tag, data, children), number, key)`
return `_o(${genElement(el, state)},${state.onceId++},${key})`
} else {
return genStatic(el, state)
}
}
# genFor
export function genFor(
el: any,
state:CodegenState,
altGen?: Function
altHelper?: string
): string {
// v-for="(item, i) in arr"
// { for: arr, alias: item, iterator1: i, iterator2: '' }
// el.for 可以理解为 "item in arr" 这个字符串
const exp = el.for
const alias = el.alias
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
// 表示已经被 genFor 处理过
el.forProcessed = true
// 得到`_l(exp, (alias, iterator1, iterator2) { return _c(tag, data, children) })`
return `${altHelper || '_l'}((${exp})),` +
`function(${alias}${iteratior1}${iterator2}){` +
`return ${(altGen || genElement)}(el, state)}` +
'})'
}
# genIf
- 生成三元表达式
export function genIf(
el: any,
state: CodegenState,
altGen?: Function
altEmpty?: string
): string {
el.ifProcessed = true
return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
}
function genIfConditions(
conditions,
state,
altGen,
altEmpty
) {
if (!conditions.length) {
return altEmpty || '_e()'
}
const condition = conditions.shift()
if (condition.exp) {
// 如果 condition.exp 条件成立, 则得到一个三元表达式
// 如果条件不成立 则递归的方式一直将单元写到条件成立的位置
return `(${condition.exp})?${genTernaryExp(condition.block)
}:${genIfConditions(conditions, state, altGen, altEmpty)}`
} else {
return `${genTernaryExp(condition.block)}`
}
}
function genTernaryExp(el) {
return el.once ? genOnce(el.state) : genElement(el, state)
}
# genComponent
function genComponent(
componentName,
el,
state
) {
const children = el.inlineTemplate ? null : genChildren(el, state, true)
return `_c(${componentName},${genData(el, state)},${children ? `,${children}` : ''})`
}
← 编译器 Render-help →