[Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串 (2)

生成器入口函数就比较简单,先初始化一些配置options,然后传入ast进行生成,没有ast时直接生成一个空div,最后返回生成的JavaScript字符串和静态节点的渲染函数。这里会把静态节点区分出来,便于后面的Vnode diff,即Vue比较算法更新DOM,现在先略过。

现在我们重点看看genElement函数,代码位置在src/compiler/codegen/index.js

export function genElement (el: ASTElement, state: CodegenState): string { 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) // v-once处理生成函数 } else if (el.for && !el.forProcessed) { return genFor(el, state) // v-for处理生成函数 } else if (el.if && !el.ifProcessed) { return genIf(el, state) // v-if处理生成函数 } else if (el.tag === 'template' && !el.slotTarget && !state.pre) { return genChildren(el, state) || 'void 0' // 子节点处理生成函数 } else if (el.tag === 'slot') { return genSlot(el, state) // slot处理生成函数 } else { // component or element let code if (el.component) { code = genComponent(el.component, el, state) // component组件处理生成函数 } else { let data if (!el.plain || (el.pre && state.maybeComponent(el))) { data = genData(el, state) // attributes节点属性处理生成函数 } const children = el.inlineTemplate ? null : genChildren(el, state, true) code = `_c('${el.tag}'${ data ? `,${data}` : '' // data }${ children ? `,${children}` : '' // children })` } // module transforms for (let i = 0; i < state.transforms.length; i++) { code = state.transforms[i](el, code) } return code } }

你会发现genElement函数里面有很多条件判断。这是因为Vue里面的指令写法实在太多,像 v-if 、 v-for 、 v-slot等,每种指令写法都分离出一个函数来单独处理,这样代码阅读起来也清晰明了。下面,我们重点来看看 genFor 和 genData 的具体处理逻辑。

genFor

genFor 函数是用来处理 v-for 指令写法的,源码位置在src/compiler/codegen/index.js

export function genFor ( el: any, state: CodegenState, altGen?: Function, altHelper?: string ): string { const exp = el.for const alias = el.alias const iterator1 = el.iterator1 ? `,${el.iterator1}` : '' const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' if (process.env.NODE_ENV !== 'production' && state.maybeComponent(el) && el.tag !== 'slot' && el.tag !== 'template' && !el.key ) { state.warn( `<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` + `v-for should have explicit keys. ` + `See https://vuejs.org/guide/list.html#key for more info.`, el.rawAttrsMap['v-for'], true /* tip */ ) } el.forProcessed = true // avoid recursion return `${altHelper || '_l'}((${exp}),` + `function(${alias}${iterator1}${iterator2}){` + `return ${(altGen || genElement)(el, state)}` + '})' }

genFor 的逻辑其实就是,首先 AST 元素节点中获取了和 for 相关的一些属性,然后返回了一个代码字符串。

genData

genData 函数是用来处理节点属性的,源码位置在src/compiler/codegen/index.js

export function genData (el: ASTElement, state: CodegenState): string { let data = '{' // directives first. // directives may mutate the el's other properties before they are generated. const dirs = genDirectives(el, state) if (dirs) data += dirs + ',' // key if (el.key) { data += `key:${el.key},` } // ref if (el.ref) { data += `ref:${el.ref},` } if (el.refInFor) { data += `refInFor:true,` } // pre if (el.pre) { data += `pre:true,` } // record original tag name for components using "is" attribute if (el.component) { data += `tag:"${el.tag}",` } // module data generation functions for (let i = 0; i < state.dataGenFns.length; i++) { data += state.dataGenFns[i](el) } // attributes if (el.attrs) { data += `attrs:${genProps(el.attrs)},` } // DOM props if (el.props) { data += `domProps:${genProps(el.props)},` } // event handlers if (el.events) { data += `${genHandlers(el.events, false)},` } if (el.nativeEvents) { data += `${genHandlers(el.nativeEvents, true)},` } // slot target,only for non-scoped slots if (el.slotTarget && !el.slotScope) { data += `slot:${el.slotTarget},` } // scoped slots if (el.scopedSlots) { data += `${genScopedSlots(el, el.scopedSlots, state)},` } // component v-model if (el.model) { data += `model:{value:${ el.model.value },callback:${ el.model.callback },expression:${ el.model.expression }},` } // inline-template if (el.inlineTemplate) { const inlineTemplate = genInlineTemplate(el, state) if (inlineTemplate) { data += `${inlineTemplate},` } } data = data.replace(/,$/, '') + '}' // v-bind dynamic argument wrap if (el.dynamicAttrs) { data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})` } // v-bind data wrap if (el.wrapData) { data = el.wrapData(data) } // v-on data wrap if (el.wrapListeners) { data = el.wrapListeners(data) } return data } 总结一下

代码生成是模板编译的第三步,它完成了AST到Render的转换,即将抽象语法树AST转换成可以直接执行的JavaScript字符串。

其中genElement的代码比较多,因为需要分别处理的情况非常多,这里只是对genFor和genData的代码逻辑进行了说明。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzxdg.html