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 elementvar 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)])])}"
评论
