preface

Optimizations in the compiler are simply static flags:

  • Each node is statically marked by traversing the AST object, by marking whether it is a static node, and then by further marking the static root node so that these static nodes can be skipped in subsequent updates
  • Flags the static root used to generate the render function phase, which generates the render function of the static root node

Deep source

CreateCompiler () method – entry

File Location:/src/compiler/index.js

The main one is static markup with the optimize() method.

/* All the work up to this point was just to build platform-specific compilation options, For example, web platform 1, parse HTML template into AST 2, statically mark ast tree 3, generate ast render function - static render function into code.staticrenderfns array - dynamic render function code.render - Vnode */ can be obtained by executing the render function during future rendering
export const createCompiler = createCompilerCreator(function baseCompile(template: string, options: CompilerOptions) :CompiledResult {
  The AST object of each node sets all the information about the element, such as tag information, attribute information, slot information, parent node, child node, etc. */
  const ast = parse(template.trim(), options)

  /* Optimize, traverse the AST, make static marks for each node - mark each node as a static node, and ensure that these static nodes are skipped in subsequent updates - mark static root nodes for the stage of generating the render function, generate the render function for the static root node */
  if(options.optimize ! = =false) {
    optimize(ast, options)
  }

  /* Generate render functions from the AST syntax tree like: 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 () method

File Location:/src/compiler/optimizer.js

Chief among them are the markStatic(root) and markStaticRoots(root, false) methods.

/* The objective of optimization is to traverse the generated template AST tree and detect purely static subtrees, i.e., DOM that never needs to change once these subtrees are detected: Raise them to constants so you don't need to create new nodes for them every time you re-render 2. Skip them completely during the repair process */
export function optimize (root: ? ASTElement, options: CompilerOptions) {
  if(! root)return
  isStaticKey = genStaticKeysCached(options.staticKeys || ' ')
  // Whether the platform retains labels
  isPlatformReservedTag = options.isReservedTag || no
  // first pass: mark all non-static nodes.
  // Iterate over all nodes and give each node a static attribute to indicate whether it is static
  markStatic(root)
  // second pass: mark static roots.
  /* To further mark the static root, a node is required to become a static root: - The node itself is static and has child nodes, and the child node is not just a text node, then it is marked as static root - the static root node cannot have only child nodes of static text, because this is too low yield, in which case it is always good to update it */
  markStaticRoots(root, false)}Copy the code

MarkStatic () method

File Location:/src/compiler/optimizer.js

The isStatic(node) method is used to determine whether the current node is a static node, and the children are marked by a recursive call to markStatic()

Note: If a child node is a dynamic node, then the parent node is also considered a dynamic node
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 component slot contents to static nodes to avoid: 1. Components cannot change slot nodes 2. Static slot content cannot be hot-loaded */
    if(! isPlatformReservedTag(node.tag) && node.tag ! = ='slot' &&
      node.attrsMap['inline-template'] = =null
    ) {
      // Recursive terminating condition: the node is not platform reserved tags && non-slot tags && are not inline templates
      return
    }

    // Iterate over the children, recursively calling markStatic to mark the static properties of those 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 non-static
      if(! child.static) { node.static =false}}// If the node has v-if, V-else, v-else instructions, then mark the nodes in the block as static
    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 () method

File Location:/src/compiler/optimizer.js

/* Check whether the node is static node - dynamic node: 1. Include the expression {{MSG}}, i.e. node.type === 2 2. The instructions that contain v-bind, V-if, v-for, etc. belong to the dynamic node 3. 4. The parent node is the template tag with the V-for directive - static node: this node is static except for dynamic nodes, such as text nodes, i.e. node.type === 3 */
function isStatic (node: ASTNode) :boolean {
  // node.type === 2 is an expression, such as {{MSG}}, which returns false
  if (node.type === 2) {
    return false
  }
  // node.type === 3 is a text node and returns true marked as a static node
  if (node.type === 3) {
    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 () method

File Location:/src/compiler/optimizer.js

/* To further mark the static root, a node is required to become a static root: - The node itself is a static node and has child nodes, and the child node is not just a text node, then it is marked as static root. - The static root node cannot have only child nodes of static text, because the payoff is too low. @param {ASTElement} node current node @param {Boolean} isInFor Whether the current node is wrapped in the node where the V-for instruction is located */
function markStaticRoots (node: ASTNode, isInFor: boolean) {
  if (node.type === 1) {
    if (node.static || node.once) {
      / / node is static or nodes have v - once instructions, tag node. StaticInFor = true | | false
      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, and the child is not just a text node, it is marked as static root
      node.staticRoot = true
      return
    } else {
      // Otherwise, non-static root
      node.staticRoot = false
    }

    // When the current node is not the static root, the child node is recursively traversed, 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) } }// If the node has v-if, V-else, v-else instructions, then mark the static root of the block node
    if (node.ifConditions) {
      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
        markStaticRoots(node.ifConditions[i].block, isInFor)
      }
    }
  }
}
Copy the code

conclusion

What is the process of static tagging?

  • Mark static nodes
    • Mark all element nodes recursively
    • If the node itself is static, but there are non-static child nodes, change the node to non-static
  • Mark the static root node and further mark the static root node based on the static node
    • If the node itself is a static node && has children that are not all text nodes, then it is marked as static root
    • If the node itself is not the static root, all children are recursively traversed, marking the static root among the children

What kind of nodes can be marked as static?

  • Dynamic node:

    • Inclusion expression{{ msg }}, i.e.,node.type === 2Is the dynamic node
    • The instructions that contain V-bind, V-if, v-for, and so on are dynamic nodes
    • Components and slots are dynamic nodes
    • The parent node is the template tag that contains the V-for instruction
  • Static node:

    • Static nodes except in the case of dynamic nodes, such as text nodes (node.type === 3)

The articles

  • What does vUE initialization do?
  • How to understand vUE responsiveness?
  • How does vUE update asynchronously?
  • Do you really understand the Vue global Api?
  • Did you lose the instance method in VUE?
  • Do you know Hook Event?
  • Vue compiler parsing