preface

The first part of the compiler, how to compile HTML template strings into aN AST, was explained in detail. Today we bring the second part of the compiler, optimizing the AST, also known as static markup.

The target

Learn more about the compiler’s static markup process

The source code interpretation

The entrance

/src/compiler/index.js

/** * All this work has been done with the sole purpose of building platform-specific compilation options. For example, web platform * * 1, parse HTML template into AST * 2, static mark ast tree * 3, ast generation rendering function * static rendering function into code. StaticRenderFns array * code Execute the render function to get vNode */ for the dynamic render function *
export const createCompiler = createCompilerCreator(function baseCompile (template: string, options: CompilerOptions) :CompiledResult {
  // Parse the template into an AST. Each AST object has all the information of the element, such as label information, attribute information, slot information, parent node, child node, etc.
  // For those attributes, see the start and end methods that handle the start and end tags
  const ast = parse(template.trim(), options)
  // Optimize by traversing the AST, marking each node statically
  // Mark whether each node is static or not, and then further mark the static root node
  // The static nodes can be skipped during subsequent updates
  // Mark the static root, which is used in the render function generation stage, to generate the render function of the static root node
  if(options.optimize ! = =false) {
    optimize(ast, options)
  }
  // Generate a rendering function from the AST to generate code like this: code.render = "_c('div',{attrs:{"id":"app"}},_l((arr),function(item){return _c('div',{key:item},[_v(_s(item))])}),0)"
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

Copy the code

optimize

/src/compiler/optimizer.js

/** * optimizes: * Traverses the AST, marking whether each node is static or dynamic, and then marking the static root node * so that you don't need to pay attention to these nodes during subsequent updates */
export function optimize(root: ? ASTElement, options: CompilerOptions) {
  if(! root)return
  /** * options.staticKeys = 'staticClass,staticStyle' * isStaticKey = function(val) { return map[val] } */
  isStaticKey = genStaticKeysCached(options.staticKeys || ' ')
  // The platform retains the label
  isPlatformReservedTag = options.isReservedTag || no
  // Go through all the nodes and set the static property to each node, indicating whether it is static or not
  markStatic(root)
  // For a node to become a static root, the following conditions are required:
  // If the node itself is static and has children, and the child is not just a text node, it is marked as static root
  // The static root node should not only have children of static text, because that would be too low. In this case, it should always be updated
  markStaticRoots(root, false)}Copy the code

markStatic

/src/compiler/optimizer.js

/** * set the static property on all nodes to indicate whether they are static nodes * Note: If any of the children are dynamic nodes, then the parent node is also considered dynamic *@param {*} node 
 * @returns * /
function markStatic(node: ASTNode) {
  // Use node.static to indicate whether a node is static
  node.static = isStatic(node)
  if (node.type === 1) {
    /** * Do not set the component's slot content to a static node, this can avoid: * 1, the component cannot change the slot node * 2, the static slot content fails during hot overload */
    if(! isPlatformReservedTag(node.tag) && node.tag ! = ='slot' &&
      node.attrsMap['inline-template'] = =null
    ) {
      // Recursively terminates if the node is not a platform reserved tag && nor a slot tag && nor an inline template
      return
    }
    // Iterate over the children, calling markStatic recursively to mark the static properties of the children
    for (let i = 0, l = node.children.length; i < l; i++) {
      const child = node.children[i]
      markStatic(child)
      // If the child node is non-static, update the parent node to be non-static
      if(! child.static) { node.static =false}}// If a node has the v-if, v-else-if, v-else-if, v-else-if instructions, then mark the static value of the node in the block
    if (node.ifConditions) {
      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
        const block = node.ifConditions[i].block
        markStatic(block)
        if(! block.static) { node.static =false
        }
      }
    }
  }
}

Copy the code

isStatic

/src/compiler/optimizer.js

/** * determine whether a node is static: * determine whether a node is static by using a custom node.type, 2: expression => dynamic, 3: Text => Static * All the v-bind, V-if, V-for directives are dynamic nodes * the component is dynamic node * the parent node is template tag containing v-for directive, then it is dynamic node *@param {*} node 
 * @returns boolean* /
function isStatic(node: ASTNode) :boolean {
  if (node.type === 2) { // expression
    // For example: {{MSG}}
    return false
  }
  if (node.type === 3) { // text
    return true
  }
  return!!!!! (node.pre || ( ! node.hasBindings &&// no dynamic bindings! node.if && ! node.for &&// not v-if or v-for or v-else! isBuiltInTag(node.tag) &&// not a built-in
    isPlatformReservedTag(node.tag) && // not a component! isDirectChildOfTemplateFor(node) &&Object.keys(node).every(isStaticKey)
  ))
}

Copy the code

markStaticRoots

/src/compiler/optimizer.js

For a node to become a static root, the following conditions are required: * If the node itself is static and has children that are not just a text node, then it is marked as static root * The static root cannot have only children of static text, because that would be too low a benefit, in which case it would be good to always update it * *@param { ASTElement } Node Indicates the current node *@param { boolean } IsInFor Whether the current node is wrapped in the node where the V-for directive is located */
function markStaticRoots(node: ASTNode, isInFor: boolean) {
  if (node.type === 1) {
    if (node.static || node.once) {
      Node.staticinfor = true or false if the node is static or has a V-once directive on it
      node.staticInFor = isInFor
    }

    if(node.static && node.children.length && ! ( node.children.length ===1 &&
      node.children[0].type === 3
    )) {
      // if the node itself is static and has children that are not just a text node, mark it as staticRoot => node.staticroot = true, otherwise non-static root
      node.staticRoot = true
      return
    } else {
      node.staticRoot = false
    }
    // If the current node is not a static root, recursively iterate through the children, marking the static root
    if (node.children) {
      for (let i = 0, l = node.children.length; i < l; i++) { markStaticRoots(node.children[i], isInFor || !! node.for) } }// Mark the static root for the block node if it has v-if, V-else-if, and V-else directives
    if (node.ifConditions) {
      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
        markStaticRoots(node.ifConditions[i].block, isInFor)
      }
    }
  }
}

Copy the code

conclusion

  • Interviewer: What does Vue’s compiler do briefly?

    A:

    Vue’s compiler does three things:

    • Parse the component’s HTML template into an AST object

    • Optimization, traversing the AST, making static tags for each node, marking whether it is static node or not, and then further marking the static root node, so that these static nodes can be skipped in the process of subsequent updates; The tag static root is used in the render function generation phase to generate the render function for the static root node

    • The staticRenderFns array contains all static node render functions


  • Interviewer: Tell me more about the static tagging process

    A:

    • Marking static nodes

      • Mark all element nodes recursively

      • If the node itself is static, but there are non-static child nodes, modify the node to be non-static

    • Mark the static root node, based on the static node, further mark the static root node

      • If the node itself is static && and has children && whose children are not all text nodes, it is marked as static root

      • If the node itself is not a static root, it recursively traverses all the children, marking the static root among them


  • Interviewer: What kind of nodes can be marked as static nodes?

    A:

    • Text node

    • No v-bind, V-for, or V-if instructions exist on the node

    • The component

Form a complete set of video

Vue source code interpretation (9) — compiler optimization

Please focus on

Welcome everyone to follow my gold mining account and B station, if the content has to help you, welcome everyone to like, collect + attention

link

  • Vue source code interpretation (1) – preface

  • Vue source code interpretation (2) — Vue initialization process

  • Vue source code interpretation (3) – response principle

  • Vue source code interpretation (4) — asynchronous update

  • Vue source code Interpretation (5) — Global API

  • Vue source code interpretation (6) — instance method

  • (7) — Hook Event

  • Vue source code interpretation (8) — compiler parsing

  • Vue source code interpretation (9) — compiler optimization

  • Vue source code interpretation (10) — compiler generation rendering function

  • (11) — Render helper

  • Vue source code interpretation (12) — patch

Learning exchange group

link