This is followed by the template template compilation, which mainly parsed the entry to template compilation and the internal implementation of the parseChildren method that parses template strings. After the AST object is generated, the transform method is called to transform the AST object, mainly by assigning the codegenNode attribute. The subsequent render method mainly relies on the codegenNode attribute.

Transform method call location

After parsing the AST object in the previous part, return to the baseCompile method and continue to execute the transform method

// The baseCompile function
const ast = isString(template) ? baseParse(template, options) : template
/ /... NodeTransforms directiveTransforms The composition of the array
transform(
    ast,
    extend({}, options, {
      prefixIdentifiers,
      nodeTransforms: [
        ...nodeTransforms,
        ...(options.nodeTransforms || []) // user transforms].directiveTransforms: extend(
        {},
        directiveTransforms,
        options.directiveTransforms || {} // user transforms)}))Copy the code

The transform method is called with two parameters. The first parameter ast object is the initial result of baseParse’s template parsing, and the second parameter is the options object (which contains the parsing instructions and method functions, which will be analyzed in detail when parsed to the specific call). Next, take a look inside the transform method implementation.

function transform(root, options) {
    const context = createTransformContext(root, options);
    traverseNode(root, context);
    // ...
}
Copy the code

TraverseNode loops through ast objects

Inside the transform method, we first call the createTransformContext method to generate a context object. Then call the traverseNode method, which internally parses the instructions and methods using the methods in the options argument.

export function traverseNode(node: RootNode | TemplateChildNode, context: TransformContext) {
    context.currentNode = node
    // apply transform plugins
    const { nodeTransforms } = context
    const exitFns = []
    for (let i = 0; i < nodeTransforms.length; i++) {
        const onExit = 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.currentNo
        }
    }
    // ...
}
Copy the code

NodeTransforms An array of functions that process the directive

The first is the for loop nodeTransforms array, which calls each method in turn, taking the Node object (initialized as an AST object of Type 0) and the Context object (generated by calling the createTransformContext method). Next take a look at the methods in the nodeTransforms array (take a quick look at which instruction or method each function parses, then use this example as a template to parse the called function in detail, and then any other hit functions).

// Revert to the baseCompile method
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(prefixIdentifiers)

// getBaseTransformPreset is an internal implementation of the method
export function getBaseTransformPreset(prefixIdentifiers? : boolean) :TransformPreset {
    return [
    [
      transformOnce,
      transformIf,
      transformFor,
      ...(!__BROWSER__ && prefixIdentifiers
        ? [
            // order is important
            trackVForSlotScopes,
            transformExpression
          ]
        : __BROWSER__ && __DEV__
          ? [transformExpression]
          : []),
      transformSlotOutlet,
      transformElement,
      trackSlotScopes,
      transformText
    ],
    / /... DirectiveTransforms object]}Copy the code

TransformOnce parses the V-once instruction transformIf parses the V-IF instruction transformFor parses the V-for instruction The trackVForSlotScopes, transformSlotOutlet, and trackSlotScopes methods parse slot slot transformExpression processes the expression transformElement TransformText Processes elements of type 1, root root of type 0, element of type 1, v-for of type 11, and so on

Parse root object (type=0)

The ast is initialized as a root object of type 0, looped through the methods in the nodeTransforms array, which eventually hits the transformText method, returning a custom function () => {} that assigns a value to the onExit object, pushed into the exitFns array. Now look at the internal implementation of the traverseNode method after the for loop

// traverseNode methods then switch-case to parse the ast child objects
switch (node.type) {
    case NodeTypes.COMMENT: // type = 3
      if(! context.ssr) { context.helper(CREATE_COMMENT) }break
    case NodeTypes.INTERPOLATION: // type = 5
      if(! context.ssr) { context.helper(TO_DISPLAY_STRING) }break
    case NodeTypes.IF: // type = 9 v-if
      for (let i = 0; i < node.branches.length; i++) {
        traverseNode(node.branches[i], context)
      }
      break
    case NodeTypes.IF_BRANCH: // type = 0 | 1
    case NodeTypes.FOR:
    case NodeTypes.ELEMENT:
    case NodeTypes.ROOT:
      traverseChildren(node, context)
      break
}
Copy the code

Parse dynamic data node (type=5)

When the root object of type=0 is resolved, the traverseChildren method is called with the switch-case option to resolve the child node objects in the Children array.

export function traverseChildren(parent: ParentNode, context: TransformContext) {
  let i = 0
  const nodeRemoved = () = > {
    i--
  }
  for (; i < parent.children.length; i++) {
    const child = parent.children[i]
    if (isString(child)) continue
    context.parent = parent
    context.childIndex = i
    context.onNodeRemoved = nodeRemoved
    traverseNode(child, context)
  }
}
Copy the code

As you can see, the rough implementation of the traverseChildren function is to loop through the elements of parent’s children array and then call the traverseNode method again to parse, skipping if the child is a string.

Hit the transformExpression function

Using this example as a template, the root node’s first child object is the result of a dynamic data message of type 5, so the transformExpression method is called in the nodeTransforms array

/ / transformExpression method
export const transformExpression: NodeTransform = (node, context) = > {
    if (node.type === NodeTypes.INTERPOLATION/ * * / 5) {
        node.content = processExpression(
          node.content as SimpleExpressionNode,
          context
        )
    } else if () {} / /...
}

/ / processExpression method
export function processExpression(
  node: SimpleExpressionNode,
  context: TransformContext,
  asParams = false,
  asRawStatements = false
) :ExpressionNode {
    if (__BROWSER__) {
        if (__DEV__) {
          // simple in-browser validation (same logic in 2.x)
          validateBrowserExpression(node, context, asParams, asRawStatements)
        }
        return node
    }
    // ...
}
Copy the code

This method calls the processExpression method to handle expressions, whose internal implementation is basically to check whether the Content property (i.e., message) is empty, whether new Function() is an error when executed under the current operating environment, and so on, before returning the passed Node.content. Enter the switch-case after the nodeTransforms array loop

// switch-case branch when type is 5 in the traverseNode method
context.helper(TO_DISPLAY_STRING)

// TO_DISPLAY_STRING is a Symbol object
export const TO_DISPLAY_STRING = Symbol(__DEV__ ? `toDisplayString` : ` `)


// methods
helper(name) {
    context.helpers.add(name)
    return name
}

Helpers: new Set() helpers is a Set object
Copy the code

Add key(Symbol(‘toDisplayString’) and value(1) values to the helpers property of context (New Set() object). This completes with a while loop that executes the functions in the exitFns array (which is the array that stored the results of the nodeTransforms loop)

// traverseNode then loops through the methods in the exitFns array
context.currentNode = node
let i = exitFns.length
while (i--) {
    exitFns[i]()
}
Copy the code

An object of type 5 does not return an array, so there is no method to execute. This example will then parse a text object of type 2, because type 2 is empty and no method has been hit.

Parse element node object (type=1)

Finally, the element node object of type 1 is executed

Hit the transformExpression function

The branch judgment for Node.type == NodeTypes.ELEMENT in the transformExpression is first hit in the Node.type == NodeTypes.ELEMENT ELEMENT in the nodeTransforms array loop

// transformExpression branch of the method node.type=1
else if (node.type === NodeTypes.ELEMENT) {
    for (let i = 0; i < node.props.length; i++) {
      const dir = node.props[i]
      // do not process for v-on & v-for since they are special handled
      if (dir.type === NodeTypes.DIRECTIVE/ * * / 7&& dir.name ! = ='for') {
          const exp = dir.exp
          const arg = dir.arg
          if(exp && exp.type === NodeTypes.SIMPLE_EXPRESSION && ! (dir.name ==='on' && arg)
          ) {/ *... * /}
          if(arg && arg.type === NodeTypes.SIMPLE_EXPRESSION && ! arg.isStatic) {/ *... * /}}}}Copy the code

When node.type===1, loop through the props array to check whether the type of the props object is 7 and not v-for, and exp is type 4 and not on(in this case, on is the event name). Then check whether the type of arG (attribute name) is 4 and not static (isStatic value is true in this case), so all the condition judgments are not valid (if the conditions are valid, the processExpression method is still used for some verification later).

Hit the transformElement function

The nodeTransforms array loop again hits the transformElement method

export const transformElement: NodeTransform = (node, context) = > {
  if (
    !(
      node.type === NodeTypes.ELEMENT/ * 1 * / &&
      (node.tagType === ElementTypes.ELEMENT/* 0 */ ||
        node.tagType === ElementTypes.COMPONENT)
    )
  ) {
    return
  }
  return function postTransformElement() {}}Copy the code

Node. type===1 and Node. tagType === 0, so return the postTransformElement method.

Hit the transformText function

Since node.type === 1 executes the transformText method, return () => {} custom function

export const transformText: NodeTransform = (node, context) = > {
  if (
    node.type === NodeTypes.ROOT ||
    node.type === NodeTypes.ELEMENT ||
    node.type === NodeTypes.FOR ||
    node.type === NodeTypes.IF_BRANCH
  ) {
      return () = > { / *... * /}}}Copy the code

At the end of the loop, execute switch-case to hit the Case NodeTypes.ELEMENT branch, execute the traverseChildren method to parse the child nodes (continue calling the traverseNode method to parse the child nodes), Because the inner child of the Button element in this case was a text node of type 2, the method in the nodeTransforms (the exitFns array was empty) was not hit, nor was the switching-case hit, and therefore the while loop did not execute any methods. Once the children array of type 1 is parsed, we return to the element object of type=1 and start the while loop to execute the methods in the exitFns array.

TraverseNode callback (type=1)

Execute the postTransformElement method

The first is the postTransformElement method

function postTransformElement() {
    const { tag, props } = node
    const isComponent = node.tagType === ElementTypes.COMPONENT
    const vnodeTag = isComponent
      ? resolveComponentType(node as ComponentNode, context)
      : `"${tag}"`
    const isDynamicComponent = 
    isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT
    
    let vnodeProps: VNodeCall['props']
    let vnodeChildren: VNodeCall['children']
    let vnodePatchFlag: VNodeCall['patchFlag']
    let patchFlag: number = 0
    let vnodeDynamicProps: VNodeCall['dynamicProps']
    let dynamicPropNames: string[] | undefined
    let vnodeDirectives: VNodeCall['directives']
    // ... shouldUseBlock = false
    
    // props
    if (props.length > 0) {
    const propsBuildResult = buildProps(node, context)
    // Assign the returned attribute resolution object
    vnodeProps = propsBuildResult.props // Attribute object
    patchFlag = propsBuildResult.patchFlag / / 8
    dynamicPropNames = propsBuildResult.dynamicPropNames // [name]
    const directives = propsBuildResult.directives
    vnodeDirectives =
        directives && directives.length
          ? (createArrayExpression(
              directives.map(dir= > buildDirectiveArgs(dir, context))
            ) as DirectiveArguments)
          : undefined  // undefined}}Copy the code

Node. tagType === 1(in this example, tagType is 0); VnodeTag, in this case “button”; IsDynamicComponent (false in this case because vnodeTag is a string)

BuildProps resolves the props property

The buildProps method is then called to resolve the props property

export function buildProps(
  node: ElementNode,
  context: TransformContext,
  props: ElementNode['props'] = node.props,
  ssr = false
) :{
  props: PropsExpression | undefined
  directives: DirectiveNode[]
  patchFlag: number
  dynamicPropNames: string[]
} {
    const { tag, loc: elementLoc } = node
    const isComponent = node.tagType === ElementTypes.COMPONENT
    let properties: ObjectExpression['properties'] = []
    const mergeArgs: PropsExpression[] = []
    const runtimeDirectives: DirectiveNode[] = []
    
    let patchFlag = 0
    let hasRef = false
    let hasClassBinding = false
    let hasStyleBinding = false
    let hasHydrationEventBinding = false
    let hasDynamicKeys = false
    let hasVnodeHook = false
    const dynamicPropNames: string[] = []
    
    const analyzePatchFlag = ({ key, value }: Property) = > {}
    for (let i = 0; i < props.length; i++) {
        const prop = props[i]
        if (prop.type === NodeTypes.ATTRIBUTE / * * / 6) {}
        else {
            // directives
            const { name, arg, exp, loc } = prop
            const isVBind = name === 'bind' // false
            const isVOn = name === 'on' // true
            // ...
            const directiveTransform = context.directiveTransforms[name]
            if (directiveTransform) {
                // has built-in directive transform.
                const { props, needRuntime } = directiveTransform(prop, node, context)
                // ...
            } else {
             // ...}}}}Copy the code

In buildProps approach will cycle each attribute of props, call context. DirectiveTransforms [name] corresponding method

Hit the transformOn function

The property in this case is called on, so call the transformOn method (defined in the second element of the array returned by the getBaseTransformPreset method)

/ / createSimpleExpression method
export function createSimpleExpression(
  content: SimpleExpressionNode['content'],
  isStatic: SimpleExpressionNode['isStatic'] = false,
  loc: SourceLocation = locStub,
  constType: ConstantTypes = ConstantTypes.NOT_CONSTANT
) :SimpleExpressionNode {
  return {
    type: NodeTypes.SIMPLE_EXPRESSION,
    loc,
    content,
    isStatic,
    constType: isStatic ? ConstantTypes.CAN_STRINGIFY : constType
  }
}

/ / transformOn method
export const transformOn: DirectiveTransform = (dir, node, context, augmentor) = > {
   const { loc, modifiers, arg } = dir as VOnDirectiveNode
   if(! dir.exp && ! modifiers.length) {}let eventName: ExpressionNode
   if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
      if (arg.isStatic) {
          const rawName = arg.content
          eventName = createSimpleExpression(
            toHandlerKey(camelize(rawName)),
            true,
            arg.loc
          )
      }
      else{}}else {}
   // ...
   letshouldCache: boolean = context.cacheHandlers && ! expif (exp) {
       // ...
   }
   let ret: DirectiveTransformResult = {
    props: [
      createObjectProperty(
        eventName,
        exp || createSimpleExpression(` ` () = > {}.false, loc)
      )
    ]
   }
   // ...
   return ret
}
Copy the code

An internal implementation of the transformOn method. If the property name (arG object) is of type 4 and isStatic is true, createSimpleExpression is called to create a simple expression object {type: 4, Content: onClick, constType: 3, … }. The name of the arG (event name) is handled (first dash to hump, and because it is listening on the on event, the name starts with on and the first letter of name is capitalized (in this case, name is click -> onClick). Finally, the createObjectProperty method is called.

export function createObjectProperty(
  key: Property['key'] | string,
  value: Property['value']
) :Property {
  return {
    type: NodeTypes.JS_PROPERTY, // type=16
    loc: locStub,
    key: isString(key) ? createSimpleExpression(key, true) : key,
    value
  }
}
Copy the code

The createObjectProperty method finally returns an object of type 16, an arg object with key type=4 and an exp object with value type=4, so the transformOn method returns {props: [{type: 16, key, value…}]}. Go back to the buildProps method and call the subsequent implementation of the directiveTransform(which is the transformOn function) method

// The internal implementation of the directiveTransform method after calling the buildProps function
const { props, needRuntime/* undefined */} = directiveTransform(prop, node, context) ! ssr && props.forEach(analyzePatchFlag) properties.push(... props)if (needRuntime) {}
Copy the code

Each element in the props array calls the analyzePatchFlag method in turn, and then pushes each element of the props into the Properties array. Take a look at the internal implementation of the analyzePatchFlag function

// analyzePatchFlag function defined inside the buildProps method
const analyzePatchFlag = ({ key, value }: Property) = > {
    // isStaticExp Determines the key type === 4 and the key isStatic === true
    if (isStaticExp(key)) {
        const name = key.content
        const isEventHandler = isOn(name) // Whether the event listener starts with on
        // ...
        if (name === 'ref') {
            hasRef = true
        } else if (name === 'class') {
            hasClassBinding = true
        } else if (name === 'style') {
            hasStyleBinding = true
        } else if(name ! = ='key' && !dynamicPropNames.includes(name)) {
            dynamicPropNames.push(name)
        }
        // ...
    }
    else{}}Copy the code

In this example, the analyzePatchFlag method puts key.content into the dynamicPropNames array (to collect attribute names). We then revert to the buildProps method

// The buildProps method follows
if (mergeArgs.length) {/ *... * /}
else if (properties.length) {
    propsExpression = createObjectExpression(
      dedupeProperties(properties), // Array of property objects [prop]
      elementLoc
    )
}

/ / createObjectExpression method
export function createObjectExpression(
  properties: ObjectExpression['properties'],
  loc: SourceLocation = locStub
) :ObjectExpression {
  return {
    type: NodeTypes.JS_OBJECT_EXPRESSION, // type = 15
    loc,
    properties
  }
}
Copy the code

A subsequent call to the createObjectExpression method returns an object of type 15 with properties as an array of prop.

// buildProps the last part of the method
if (hasDynamicKeys) {}
else {
    if (dynamicPropNames.length) {
      patchFlag |= PatchFlags.PROPS / / | 0 8 = 8}}// ...
return {
    props: propsExpression,
    directives: runtimeDirectives,
    patchFlag,
    dynamicPropNames
}
Copy the code

Finally, the buildProps method returns an object representing further parsing of the property {props: {… }, patchFlag: 8, … }, then go back to the transformElement method, and after parsing the properties, start processing the Children array

// The transformElement method handles children subsequent
if (node.children.length > 0) {
    // ...
    constshouldBuildAsSlots = isComponent && vnodeTag ! == TELEPORT && vnodeTag ! == KEEP_ALIVEif (shouldBuildAsSlots) {}
    else if (node.children.length === 1&& vnodeTag ! == TELEPORT) {const child = node.children[0]
        const type = child.type
        const hasDynamicTextChild /* false */ =
          type === NodeTypes.INTERPOLATION ||
          type === NodeTypes.COMPOUND_EXPRESSION
        // ...
        if (hasDynamicTextChild || type === NodeTypes.TEXT) {
          vnodeChildren = child as TemplateTextChildNode
        } else {
          vnodeChildren = node.children
        }
    } else{}}if(patchFlag ! = =0) {
    if (__DEV__) {
        if (patchFlag < 0) {/* patchFlag = 8 */} 
        else {
            const flagNames = Object.keys(PatchFlagNames)
            .map(Number)
            .filter(n= > n > 0 && patchFlag & n)
            .map(n= > PatchFlagNames[n])
            .join(`, `)
            // PatchFlagNames object {8: 'PROPS'}
            vnodePatchFlag = patchFlag + ` / *${flagNames}* / `}}else {
        vnodePatchFlag = String(patchFlag)
    }
    if (dynamicPropNames && dynamicPropNames.length) {
        vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames) 
        // Add [] -> "["onClick"]"} } node.codegenNode = createVNodeCall( context, vnodeTag, vnodeProps, vnodeChildren, vnodePatchFlag, vnodeDynamicProps, vnodeDirectives, !! shouldUseBlock,false /* disableTracking */,
  node.loc
)
Copy the code

Child =node.children[0], vnodePatchFlag=”8 /* PROPS */”, VnodeDynamicProps =[“onClick”], and then call the createVNodeCall method to create the codegenNode property object

export function createVNodeCall(
  context: TransformContext | null,
  tag: VNodeCall['tag'], props? : VNodeCall['props'], children? : VNodeCall['children'], patchFlag? : VNodeCall['patchFlag'], dynamicProps? : VNodeCall['dynamicProps'], directives? : VNodeCall['directives'],
  isBlock: VNodeCall['isBlock'] = false,
  disableTracking: VNodeCall['disableTracking'] = false,
  loc = locStub
) :VNodeCall {
  if (context) {
    if (isBlock) {
      context.helper(OPEN_BLOCK)
      context.helper(CREATE_BLOCK)
    } else {
      context.helper(CREATE_VNODE)
    }
    if (directives) {
      context.helper(WITH_DIRECTIVES)
    }
  }

  return {
    type: NodeTypes.VNODE_CALL, // type = 13
    tag,
    props,
    children,
    patchFlag,
    dynamicProps,
    directives,
    isBlock,
    disableTracking,
    loc
  }
}
/ / CREATE_VNODE definition
export const CREATE_VNODE = Symbol(__DEV__ ? `createVNode` : ` `)
Copy the code

The createVNodeCall method internally determines whether isBlock is true, which in this case is false, so add Symbol(‘createVNode’) to the helpers object in Contetx and return an object of type 13

Executes the custom method returned in the transformText function

After executing the postTransformElement function, proceed with the custom () => {} method and take a look at the internal implementation of this method

() = > {const children = node.children
    let currentContainer: CompoundExpressionNode | undefined = undefined
    let hasText = false
    
    for (let i = 0; i < children.length; i++) {
        const child = children[i]
        // isText: node.type === 5 || 2
        if (isText(child)) {
            hasText = true
            for (let j = i + 1; j < children.length; j++) {
                const next = children[j]
                if (isText(next)) {
                    if(! currentContainer) { currentContainer = children[i] = {type: NodeTypes.COMPOUND_EXPRESSION,
                            loc: child.loc,
                            children: [child]
                        }
                    }
                    // merge adjacent text node into current
                    currentContainer.children.push(` + `, next)
                    children.splice(j, 1)
                    j--
                } else {
                    currentContainer = undefined
                    break
                }
            }
        }
    }
}
Copy the code

Type = 5 or 2), and then determine whether the child’s next element is also a text node. Children of type 1 is 1 in length (only one child node). So break out of the for loop and execute the following code

() = > {// Internal implementation of a custom function after the for loop
    if(! hasText || (children.length ===1 && (node.type === NodeTypes.ROOT || (node.type === NodeTypes.ELEMENT &&
      node.tagType === ElementTypes.ELEMENT)))
      ) {
      return}}Copy the code

Using this example as a template, the element of type 1 satisfies the children array of length 1 and Node. tagType === 0, so return. At this point, the callbacks in the exitFns array of type 1 have all been executed, generating the codegenNode property value (an object of type 13) with further analysis of both the props property and the children node.

TraverseNode callback (type=0)

() => {… () => {… () => {… }. The first is the cyclic children array (the children array of the root node of type 0 is objects of type 5,2,1). The internal implementation of the for loop is roughly to replace the child node with a new object of type 8 if both the child and its next element are text nodes, The children attribute of the new object is [child, ‘+’, next(the next element of child)] (an array of three elements), So now the object of type 0 children attribute value into [{type: 8, children: [{type: 5,…}, ‘+’ {2} type:],…}, {type: 1, …}] (two adjacent text objects of type 5 and 2 are replaced with objects of type 8). Finally, the end of the custom function is executed.

// createCallExpression method definition
export function createCallExpression<T extends CallExpression['callee'] > (
  callee: T,
  args: CallExpression['arguments'] = [],
  loc: SourceLocation = locStub
) :InferCodegenNodeType<T> {
  return {
    type: NodeTypes.JS_CALL_EXPRESSION, // type = 14
    loc,
    callee,
    arguments: args
  } as any
}

// The root object of type 0 performs the subsequent partial implementation of the custom function() = > {// ...
    for (let i = 0; i < children.length; i++) {
        const child = children[i]
        if (isText(child) || child.type === NodeTypes.COMPOUND_EXPRESSION) {
          const callArgs: CallExpression['arguments'] = []
          // createTextVNode defaults to single whitespace, so if it is a
          // single space the code could be an empty call to save bytes.
          if(child.type ! == NodeTypes.TEXT || child.content ! = =' ') {
            callArgs.push(child)
          }
          // mark dynamic text with flag so it gets patched inside a block
          if (
            !context.ssr &&
            getConstantType(child, context) === ConstantTypes.NOT_CONSTANT
          ) {
            callArgs.push(
              PatchFlags.TEXT +
                (__DEV__ ? ` / *${PatchFlagNames[PatchFlags.TEXT]}* / ` : ` `)
            )
          }
          children[i] = {
            type: NodeTypes.TEXT_CALL, // type = 12
            content: child,
            loc: child.loc,
            codegenNode: createCallExpression(
              context.helper(CREATE_TEXT), 
              // export const CREATE_TEXT = Symbol(__DEV__ ? `createTextVNode` : ``)
              callArgs
           )
         }
       }
    }
}
Copy the code

The final part again loops through the Children array (in this case, objects of type 8 and 1), further converting objects of type 8. CallArgs =1; callArgs =1; callArgs =1; callArgs =1; callArgs =1; The codegenNode attribute of the new object is assigned to the createCallExpression method return value {type: 14, Callee: Symbol(‘createTextVNode’), arguments: callArgs array,… }. So the callback of type 0 is done (basically a merge of the text node object). Return to the transform method and, once the traverseNode function has been executed, look at the subsequent internal implementation.

if (options.hoistStatic/* true */) {
    hoistStatic(root, context)
}
if(! options.ssr) { createRootCodegen(root, context) }// finalize meta information
root.helpers = [...context.helpers]
root.components = [...context.components]
root.directives = [...context.directives]
root.imports = context.imports
root.hoists = context.hoists
root.temps = context.temps
root.cached = context.cached

// hoistStatic method definition
export function hoistStatic(root: RootNode, context: TransformContext) {
  walk(
    root,
    context,
    // Root node is unfortunately non-hoistable due to potential parent
    // fallthrough attributes.
    isSingleElementRoot(root, root.children[0]) // false(children.length ! = = 1))}Copy the code

The walk method is called first.

CreateRootCodegen creates the root codegenNode attribute

The createRootCodegen method is then called

function createRootCodegen(root: RootNode, context: TransformContext) {
  const { helper } = context
  const { children } = root
  if (children.length === 1) {}else if (children.length > 1) {
      let patchFlag = PatchFlags.STABLE_FRAGMENT / / 64
      let patchFlagText = PatchFlagNames[PatchFlags.STABLE_FRAGMENT] // 'STABLE_FRAGMENT'
      root.codegenNode = createVNodeCall(
          context,
          helper(FRAGMENT), // export const FRAGMENT = Symbol(__DEV__ ? `Fragment` : ``)
          undefined,
          root.children,
          patchFlag + (__DEV__ ? ` / *${patchFlagText}* / ` : ` `),
          undefined.undefined.true // isBlock)}}Copy the code

This method internally calls the createVNodeCall function, which, as mentioned above, ultimately returns an object of type 13 because isBlock is true, Add Symbol(‘openBlock’) and Symbol(‘createBlock’) to the helpers(Set object) of context object. PatchFlag = 64, isBlock = true Children is root.children(an array of children of type 0), and tag is Symbol(‘Fragment’). Finally, assign some objects from the context to the root object.

conclusion

The Transform method takes the AST object parsed by the baseParse function as the source for further transformation. TraverseNode functions (which are cycled when the root object or element object is traversed and the child node object is parsed). Internally, this method first observes whether the instruction function is matched and then collects the callback function for cyclic execution, during which the attributes of the element node are parsed (using this example as a template). @click hits transformOn handling event listeners, storing methods and event callbacks); Child nodes are parsed (merging two adjacent text nodes to produce a new object), and so on. Generate the codegenNode property on the root node, which contains the children node, properties props, patchFlag processing flag bits, and so on. Finally, transfer some properties of the context to the root object, paving the way for the generate method (such as helpers object, which holds the node creation method exposed by Vue) : ToDisplayString creates dynamic data, createVNode creates a node, and createTextVNode creates a text node. I’ll explain how the generate function uses the codegenNode property to assemble the Render function string.