vue中ast生成render
我们先使用 vue 的模板编译库 vue-template-compiler 验证一下输出结果。
const compiler = require('vue-template-compiler')
let str = `<div v-if="msg">999</div>`
console.log(compiler.compile(str))
发现包含 ast 和 render 这两个属性。
{
ast: {
type: 1,
tag: 'div',
attrsList: [],
attrsMap: { 'v-if': 'msg' },
rawAttrsMap: {},
parent: undefined,
children: [ [Object] ],
if: 'msg',
ifConditions: [ [Object] ],
plain: true,
static: false,
staticRoot: false,
ifProcessed: true
},
render: `with(this){return (msg)?_c('div',[_v("999")]):_e()}`,
staticRenderFns: [],
errors: [],
tips: []
}
render 属性的内容是根据 ast 属性生成的,之前我有文章已经讲解了 ast 是如何生成的,今天主要分析 render 函数的生成过程。
// <div v-if="msg">999</div>
with(this){return (msg)?_c('div',[_v("999")]):_e()}
每个指令的生成过程都不一样,if 指令会使用三元运算符,for 指令会生成一个新的函数,并且会使用一些辅助函数 _c 和 _v 等等。
// <div><span v-for="item in list">6</span></div>
with(this){return _c('div',_l((list),function(item){return _c('span',[_v("6")])}),0)}
开始了解如何生成 render 函数
function generate (
ast,
options
) {
var code = ast ? (ast.tag === 'script' ? 'null' : genElement(ast, options)) : '_c("div")';
return {
render: ("with(this){return " + code + "}")
}
}
我们暂时先了解 genElement 函数,看看如何生成元素的。
function genElement (el, state) {
if (el.parent) {
el.pre = el.pre || el.parent.pre;
}
if (el.staticRoot && !el.staticProcessed) {
// return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
// return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
// return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
// return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
// return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
// return genSlot(el, state)
} else {
// component or element
var code;
if (el.component) {
// code = genComponent(el.component, el, state);
} else {
var data;
var children = el.inlineTemplate ? null : genChildren(el, state, true);
code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")";
}
// module transforms
//for (var i = 0; i < state.transforms.length; i++) {
// code = state.transforms[i](el, code);
//}
return code
}
}
在生成元素的过程中,会先判断元素上面的各种指令进行分别的处理,指令的生成我们暂时不讨论,我们先看看 genChildren 的处理。
function genChildren (
el,
state,
checkSkip,
altGenElement,
altGenNode
) {
var children = el.children;
if (children.length) {
var gen = altGenNode || genNode;
return ("[" + (children.map(function (c) { return gen(c, state); }).join(',')) + "]")
}
}
// genNode 函数是根据不同的 type 生成不同的内容
function genNode (node, state) {
if (node.type === 1) {
// 如果子元素还是元素会递归的处理
return genElement(node, state)
} else if (node.type === 3 && node.isComment) {
// return genComment(node)
} else {
// 生成文本
return genText(node)
}
}
function genText (text) {
return ("_v(" + (text.type === 2
? text.expression // no need for () because already wrapped in _s()
: JSON.stringify(text.text)) + ")")
}
最后执行我们的代码,验证是否会成功:
template: '<div><span>11</span></div>'
render: "with(this){return _c('div',[_c('span',[_v(11)])])}"
评论