functionwalk(node: ParentNode,// 当前节点
context: TransformContext,// 节点上下文
doNotHoistNode: boolean=false// 是否可以被提升
){const{children}=nodeconstoriginalCount=children.lengthlethoistedCount=0// 遍历子元素
for(leti=0;i<children.length;i++){constchild=children[i]// only plain elements & text calls are eligible for hoisting.
// 只有纯元素节点或者文本节点才会被提升
if(child.type===NodeTypes.ELEMENT&&child.tagType===ElementTypes.ELEMENT){constconstantType=doNotHoistNode?ConstantTypes.NOT_CONSTANT : getConstantType(child,context)if(constantType>ConstantTypes.NOT_CONSTANT){// 可以提升情况下
if(constantType>=ConstantTypes.CAN_HOIST){// 打上patch flag
;(child.codegenNodeasVNodeCall).patchFlag=PatchFlags.HOISTED+(__DEV__?` /* HOISTED */`:``)// 提升后代码
child.codegenNode=context.hoist(child.codegenNode!)// 提升数量+1
hoistedCount++continue}}else{// node may contain dynamic children, but its props may be eligible for
// hoisting.
// 元素是动态元素,但是props存在可被提升的prop
constcodegenNode=child.codegenNode!if(codegenNode.type===NodeTypes.VNODE_CALL){constflag=getPatchFlag(codegenNode)if((!flag||flag===PatchFlags.NEED_PATCH||flag===PatchFlags.TEXT)&&getGeneratedPropsConstantType(child,context)>=ConstantTypes.CAN_HOIST){// 提升props
constprops=getNodeProps(child)if(props){codegenNode.props=context.hoist(props)}}// 提升动态props
if(codegenNode.dynamicProps){codegenNode.dynamicProps=context.hoist(codegenNode.dynamicProps)}}}}// walk further
if(child.type===NodeTypes.ELEMENT){constisComponent=child.tagType===ElementTypes.COMPONENTif(isComponent){context.scopes.vSlot++}walk(child,context)if(isComponent){context.scopes.vSlot--}}elseif(child.type===NodeTypes.FOR){// Do not hoist v-for single child because it has to be a block
// 执行v-for
walk(child,context,child.children.length===1)}elseif(child.type===NodeTypes.IF){for(leti=0;i<child.branches.length;i++){// Do not hoist v-if single child because it has to be a block
// 执行v-if
walk(child.branches[i],context,child.branches[i].children.length===1)}}}if(hoistedCount&&context.transformHoist){context.transformHoist(children,context,node)}// all children were hoisted - the entire children array is hoistable.
// 整个子元素都可以提升的情况下
if(hoistedCount&&hoistedCount===originalCount&&node.type===NodeTypes.ELEMENT&&node.tagType===ElementTypes.ELEMENT&&node.codegenNode&&node.codegenNode.type===NodeTypes.VNODE_CALL&&isArray(node.codegenNode.children)){// 重新创建子节点
node.codegenNode.children=context.hoist(createArrayExpression(node.codegenNode.children))}}
/**
* 获取原始node类型
* @param node
* @param context
* @returns
*/exportfunctiongetConstantType(node: TemplateChildNode|SimpleExpressionNode,context: TransformContext):ConstantTypes{const{constantCache}=contextswitch(node.type){// 元素
caseNodeTypes.ELEMENT:// 非原生元素,不能提升
if(node.tagType!==ElementTypes.ELEMENT){returnConstantTypes.NOT_CONSTANT}constcached=constantCache.get(node)// 存在缓存
if(cached!==undefined){returncached}constcodegenNode=node.codegenNode!// 不是vnode_call 不能提升
if(codegenNode.type!==NodeTypes.VNODE_CALL){returnConstantTypes.NOT_CONSTANT}//
if(codegenNode.isBlock&&node.tag!=='svg'&&node.tag!=='foreignObject'){returnConstantTypes.NOT_CONSTANT}constflag=getPatchFlag(codegenNode)if(!flag){letreturnType=ConstantTypes.CAN_STRINGIFY// Element itself has no patch flag. However we still need to check:
// 1. Even for a node with no patch flag, it is possible for it to contain
// non-hoistable expressions that refers to scope variables, e.g. compiler
// injected keys or cached event handlers. Therefore we need to always
// check the codegenNode's props to be sure.
// 获取props执行后的类型
constgeneratedPropsType=getGeneratedPropsConstantType(node,context)if(generatedPropsType===ConstantTypes.NOT_CONSTANT){// 存入缓存,不能提升
constantCache.set(node,ConstantTypes.NOT_CONSTANT)returnConstantTypes.NOT_CONSTANT}if(generatedPropsType<returnType){returnType=generatedPropsType}// 2. its children.
// 子元素
for(leti=0;i<node.children.length;i++){constchildType=getConstantType(node.children[i],context)if(childType===ConstantTypes.NOT_CONSTANT){constantCache.set(node,ConstantTypes.NOT_CONSTANT)returnConstantTypes.NOT_CONSTANT}if(childType<returnType){returnType=childType}}// 3. if the type is not already CAN_SKIP_PATCH which is the lowest non-0
// type, check if any of the props can cause the type to be lowered
// we can skip can_patch because it's guaranteed by the absence of a
// patchFlag.
if(returnType>ConstantTypes.CAN_SKIP_PATCH){for(leti=0;i<node.props.length;i++){constp=node.props[i]if(p.type===NodeTypes.DIRECTIVE&&p.name==='bind'&&p.exp){constexpType=getConstantType(p.exp,context)if(expType===ConstantTypes.NOT_CONSTANT){constantCache.set(node,ConstantTypes.NOT_CONSTANT)returnConstantTypes.NOT_CONSTANT}if(expType<returnType){returnType=expType}}}}// only svg/foreignObject could be block here, however if they are
// static then they don't need to be blocks since there will be no
// nested updates.
if(codegenNode.isBlock){// except set custom directives.
for(leti=0;i<node.props.length;i++){constp=node.props[i]if(p.type===NodeTypes.DIRECTIVE){constantCache.set(node,ConstantTypes.NOT_CONSTANT)returnConstantTypes.NOT_CONSTANT}}context.removeHelper(OPEN_BLOCK)context.removeHelper(getVNodeBlockHelper(context.inSSR,codegenNode.isComponent))codegenNode.isBlock=falsecontext.helper(getVNodeHelper(context.inSSR,codegenNode.isComponent))}constantCache.set(node,returnType)returnreturnType}else{constantCache.set(node,ConstantTypes.NOT_CONSTANT)returnConstantTypes.NOT_CONSTANT}caseNodeTypes.TEXT:
caseNodeTypes.COMMENT:
returnConstantTypes.CAN_STRINGIFY// if-for 不能提升
caseNodeTypes.IF:
caseNodeTypes.FOR:
caseNodeTypes.IF_BRANCH:
returnConstantTypes.NOT_CONSTANTcaseNodeTypes.INTERPOLATION:
caseNodeTypes.TEXT_CALL:
returngetConstantType(node.content,context)// 表达式
caseNodeTypes.SIMPLE_EXPRESSION:
returnnode.constTypecaseNodeTypes.COMPOUND_EXPRESSION:
letreturnType=ConstantTypes.CAN_STRINGIFYfor(leti=0;i<node.children.length;i++){constchild=node.children[i]if(isString(child)||isSymbol(child)){continue}constchildType=getConstantType(child,context)if(childType===ConstantTypes.NOT_CONSTANT){returnConstantTypes.NOT_CONSTANT}elseif(childType<returnType){returnType=childType}}returnreturnType// 默认不给提升
default:if(__DEV__){constexhaustiveCheck: never=nodeexhaustiveCheck}returnConstantTypes.NOT_CONSTANT}}
constallowHoistedHelperSet=newSet([NORMALIZE_CLASS,NORMALIZE_STYLE,NORMALIZE_PROPS,GUARD_REACTIVE_PROPS])functiongetConstantTypeOfHelperCall(value: CallExpression,context: TransformContext):ConstantTypes{if(value.type===NodeTypes.JS_CALL_EXPRESSION&&!isString(value.callee)&&allowHoistedHelperSet.has(value.callee)){constarg=value.arguments[0]asJSChildNodeif(arg.type===NodeTypes.SIMPLE_EXPRESSION){returngetConstantType(arg,context)}elseif(arg.type===NodeTypes.JS_CALL_EXPRESSION){// in the case of nested helper call, e.g. `normalizeProps(guardReactiveProps(exp))`
returngetConstantTypeOfHelperCall(arg,context)}}returnConstantTypes.NOT_CONSTANT}functiongetGeneratedPropsConstantType(node: PlainElementNode,context: TransformContext):ConstantTypes{letreturnType=ConstantTypes.CAN_STRINGIFYconstprops=getNodeProps(node)// props存在并且是原始js对象
if(props&&props.type===NodeTypes.JS_OBJECT_EXPRESSION){const{properties}=propsfor(leti=0;i<properties.length;i++){const{key,value}=properties[i]// 获取key类型
constkeyType=getConstantType(key,context)if(keyType===ConstantTypes.NOT_CONSTANT){returnkeyType}if(keyType<returnType){returnType=keyType}letvalueType: ConstantTypes// 获取value类型
if(value.type===NodeTypes.SIMPLE_EXPRESSION){valueType=getConstantType(value,context)}elseif(value.type===NodeTypes.JS_CALL_EXPRESSION){// some helper calls can be hoisted,
// such as the `normalizeProps` generated by the compiler for pre-normalize class,
// in this case we need to respect the ConstantType of the helper's arguments
// js执行表达式
valueType=getConstantTypeOfHelperCall(value,context)}else{valueType=ConstantTypes.NOT_CONSTANT}// key类型和value类型中有一个不能提升就不给提升
if(valueType===ConstantTypes.NOT_CONSTANT){returnvalueType}if(valueType<returnType){returnType=valueType}}}returnreturnType}functiongetNodeProps(node: PlainElementNode){constcodegenNode=node.codegenNode!if(codegenNode.type===NodeTypes.VNODE_CALL){returncodegenNode.props}}functiongetPatchFlag(node: VNodeCall):number|undefined{constflag=node.patchFlagreturnflag?parseInt(flag,10):undefined}