模板编译流程Vue3模板编译就是将模板字符串编译成渲染函数//template
//renderimport{toDisplayStringas_toDisplayString,createElementVNodeas_createElementVNode,openBlockas_openBlock,createElementBlockas_createElementBlock}from"vue"导出函数render(_ctx,_cache,$props,$setup,$data,$options){return(_openBlock(),_createElementBlock("div",null,[_createElementVNode("p",null,_toDisplayString(_ctx.LH_R),1/*TEXT*/)]))}我会按照编译过程分3步分析parse:converttemplatestringtotemplateASTtransform:converttemplateASTtoASTgeneratethat描述渲染函数:生成基于AST的渲染函数baseParse(template,options):templateconst[nodeTransforms,directiveTransforms]=getBaseTransformPreset(prefixIdentifiers)转换(ast,extend({},options,{prefixIdentifiers,nodeTransforms:[...nodeTransforms,...(options.nodeTransforms||[])//用户转换],directiveTransforms:extend({},directiveTransforms,options.directiveTransforms||{}//用户转换)}))returngenerate(ast,extend({},options,{prefixIdentifiers}))}parseparse遍历模板字符串,然后循环判断开始标签和结束标签将字符串拆分成token。有一个tokenlist,然后scantokenlist维护一个startlabelstack。每当扫描到起始标签节点时,它就会被推到堆栈的顶部。堆栈顶部的节点始终是下一个扫描节点的父节点。这样,当所有的Token都扫描完后,就可以构建一个树形的AST。下面是parseChildren源码的简化版,是parse的主要入口。functionparseChildren(context:ParserContext,mode:TextModes,ancestors:ElementNode[]//节点栈结构,用于维护节点嵌套关系):TemplateChildNode[]{//获取父节点constparent=last(ancestors)constns=父母?parent.ns:Namespaces.HTMLconstnodes:TemplateChildNode[]=[]//存储已解析的AST子节点//遇到结束标记时结束解析while(!isEnd(context,mode,ancestors)){//剪切模板字符串consts=context.sourceletnode:TemplateChild节点|模板子节点[]|undefined=undefinedif(mode===TextModes.DATA||mode===TextModes.RCDATA){if(!context.inVPre&&startsWith(s,context.options.delimiters[0])){//解析插值表达式{{}}node=parseInterpolation(context,mode)}elseif(mode===TextModes.DATA&&s[0]==='<'){if(s[1]==='!'){//解析注释节点和文档声明...}elseif(s[1]==='/'){if(s[2]==='>'){//对于自闭合标签,提前三个字符advanceBy(context,3)continue}elseif(/[a-z]/i.test(s[2])){//解析结束标签parseTag(context,TagType.End,parent)continue}else{//如果不满足以上条件,则解析为伪注释node=parseBogusComment(context)}}elseif(/[a-z]/i.test(s[1])){//解析html开始label,获取解析后的AST节点node=parseElement(context,ancestors)}}}if(!node){//普通文本节点node=parseText(context,mode)}//如果节点是数组,则遍历并添加到节点中if(isArray(node)){for(leti=0;i
LH_R