transform(ast,extend({},options,{prefixIdentifiers,nodeTransforms:[...nodeTransforms,...(options.nodeTransforms||[])// user transforms
],directiveTransforms: extend({},directiveTransforms,options.directiveTransforms||{}// user transforms
)}))
exportfunctiontraverseNode(node: RootNode|TemplateChildNode,context: TransformContext){// 使用currentNode记录当前操作的node
context.currentNode=node// apply transform plugins
// 拿到transform插件数组
const{nodeTransforms}=contextconstexitFns=[]for(leti=0;i<nodeTransforms.length;i++){// 每个单独执行
constonExit=nodeTransforms[i](node,context)if(onExit){// 如果返回结束函数的,进行搜集
if(isArray(onExit)){exitFns.push(...onExit)}else{exitFns.push(onExit)}}// 容错,如果没有当前节点了,进行覆盖,保持原节点
if(!context.currentNode){// node was removed
return}else{// node may have been replaced
node=context.currentNode}}switch(node.type){caseNodeTypes.COMMENT:
if(!context.ssr){// inject import for the Comment symbol, which is needed for creating
// comment nodes with `createVNode`
// 使用createCommentVNode创建静态节点
context.helper(CREATE_COMMENT)}breakcaseNodeTypes.INTERPOLATION:// no need to traverse, but we need to inject toString helper
if(!context.ssr){// 插值表达式,不需要转换,但是需要转换成参数
context.helper(TO_DISPLAY_STRING)}break// for container types, further traverse downwards
caseNodeTypes.IF:// if 节点,每个分支进行转换
for(leti=0;i<node.branches.length;i++){traverseNode(node.branches[i],context)}breakcaseNodeTypes.IF_BRANCH:
caseNodeTypes.FOR:
caseNodeTypes.ELEMENT:
caseNodeTypes.ROOT:// 节点子元素进行递归转换
traverseChildren(node,context)break}// exit transforms
// https://github.com/vuejs/core/issues/2035
// 修复v-once 和条件分支并存的情况
context.currentNode=node// 循环执行退出函数
leti=exitFns.lengthwhile(i--){exitFns[i]()}}