虚拟DOM渲染函数生成
Vue通过 codegen 将AST转换为渲染函数字符串,经 Function 构造创建可执行函数,生成虚拟DOM树。
codegen 入口
JavaScript
// src/compiler/codegen/index.js
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
}
}
AST根节点传入 genElement 生成渲染代码字符串。
genElement 元素生成
JavaScript
function genElement(el, state) {
// 静态节点优化
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
}
// v-once 指令
else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
}
// v-if 指令
else if (el.if && !el.ifProcessed) {
return genIf(el, state)
}
// v-for 指令
else if (el.for && !el.forProcessed) {
return genFor(el, state)
}
// slot 标签
else if (el.tag === 'slot') {
return genSlot(el, state)
}
// 普通元素
else {
let code
if (el.component) {
code = genComponent(el.tag, el, state)
} else {
const data = el.plain ? undefined : genData(el, state)
const children = genChildren(el, state)
code = `_c('${el.tag}'${data ? `,${data}` : ''}${children ? `,${children}` : ''})`
}
return code
}
}
按指令优先级依次处理,普通元素调用 genData 和 genChildren 生成属性与子节点代码。
genData 数据属性生成
JavaScript
function genData(el, state) {
let data = '{'
// key
if (el.key) {
data += `key:${el.key},`
}
// ref
if (el.ref) {
data += `ref:${el.ref},`
}
// attrs
if (el.attrs) {
data += `attrs:${genProps(el.attrs)},`
}
// props
if (el.props) {
data += `domProps:${genProps(el.props)},`
}
// on
if (el.events) {
data += `${genHandlers(el.events, false)},`
}
// directives
if (el.directives) {
data += `directives:${genDirectives(el.directives)},`
}
return data + '}'
}
按属性类型分别生成 attrs、domProps、on、directives 等配置。
genChildren 子节点生成
JavaScript
function genChildren(el, state) {
const children = el.children
if (children.length === 0) return undefined
// 单节点优化
if (children.length === 1) {
return genNode(children[0], state)
}
// 多节点
return `[${children.map(c => genNode(c, state)).join(',')}]`
}
function genNode(node, state) {
if (node.type === 1) return genElement(node, state)
if (node.type === 3) return node.isComment ? '_e()' : `_v(${text})`
if (node.type === 2) return `_v(${node.expression})`
}
单节点直接返回,多节点包装为数组。
辅助函数映射
JavaScript
// 渲染函数辅助函数
{
'_c': createElement, // 创建元素VNode
'_v': createTextVNode, // 创建文本VNode
'_e': createEmptyVNode, // 创建空注释VNode
'_u': resolveScopedSlots, // 解析作用域插槽
'_m': renderStatic, // 渲染静态节点
'_l': renderList, // 渲染v-for列表
'_s': toString, // 值转字符串
'_t': renderSlot // 渲染插槽
}
编译后的渲染函数使用这些缩写辅助函数创建VNode。
创建可执行函数
JavaScript
// 渲染函数字符串
const render = 'with(this){return _c("div",[_c("h1",[_v("标题")]),_c("p",[_v(_s(msg))])])}'
// 创建可执行函数
const createFunction = (code, errors) => {
try {
return new Function(code)
} catch (e) {
errors.push({ err: e, code })
return null
}
}
const renderFn = createFunction(render, errors)
// 执行时: renderFn.call(vm) 生成VNode树
通过 new Function(code) 将字符串转为可执行函数,执行时通过 call(vm) 绑定实例上下文。
要点总结
generate将AST转为渲染函数字符串,使用with(this)简化访问genElement按指令优先级处理静态、v-if、v-for、slot等genData生成属性配置对象,genChildren生成子节点数组- 渲染函数使用
_c、_v、_e等辅助函数创建VNode - 静态节点通过
_m(index)调用staticRenderFns - 通过
new Function(code)创建可执行函数,call(vm)绑定上下文
📝 发现内容有误?点击此处直接编辑