vue中ast生成render

前端精髓

共 3380字,需浏览 7分钟

 ·

2021-09-15 23:21


我们先使用 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)])])}"


浏览 24
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报