This is the 26th day of my participation in the First Challenge 2022.

Previously on & Background

My last essay mainly completed the following tasks:

  1. parseHTMLThe last callback method ofoptions.endThe discussion;
  2. itcurerntParentstartendThe updating function of the
  3. Review the entireparsemethodsparseHTMLThe synopsis method and its function;

The previous section covered the process of obtaining an AST from an HTML template using the Parse method. The following sections will continue. This short article will focus on static markup optimization after the AST is generated.

Static tag calls in baseCompile

After Parse generates the AST, the optimize method is called for static markup processing.

export const createCompiler = createCompilerCreator(function baseCompile (template: string, options: CompilerOptions) :CompiledResult {
  const ast = parse(template.trim(), options)
  // Optimize for static marking of each node
  if(options.optimize ! = =false) {
    optimize(ast, options)
  }

  
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})
Copy the code

Optimize method

Methods location: SRC/compiler/optimizer. Js – > function to optimize

Method parameters:

  1. rootAnd the topastNode object;
  2. options: compiler option object

Methods:

  1. generateisStaticKeyThe function,isStaticKeyFunction is a function that checks whether certain properties are static properties,options.staticKeys'staicClass,staticStyle'This string, this string also comes frombaseOptions.baseOptionsfromcreateCompiler(baseOptions)The incoming;
    • 1.1 isStaticThe method is to receive somekeyJudge thiskeyWhether it istype,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap,staticStyle,staticClassOne of them. The implication is that these are all listedastStatic attributes;
  2. traverserootNode, set for each nodestaticProperty, which identifies whether the current node is static;

Nodes marked as static nodes will not be paid attention to during data update, and these static nodes will also be ignored during patch.

export function optimize (root: ? ASTElement, options: CompilerOptions) {
  if(! root)return
  isStaticKey = genStaticKeysCached(options.staticKeys || ' ')

  // Whether to keep the platform label
  isPlatformReservedTag = options.isReservedTag || no

  // Walk through the nodes and give each node a static attribute to indicate whether it is static
  markStatic(root)

  // Mark the static root
  markStaticRoots(root, false)}Copy the code

3.1 genStaticKeysCached

Methods location: SRC/compiler/optimizer. Js – > const genStaticKeysCached

Method argument: STR

The caches () method returns a function that takes the result from the cache first.

const genStaticKeysCached = cached(genStaticKeys)
Copy the code

3.1.1 cached

SRC /shared/util.js -> function cached

Method parameter: fn, objective function

The caches () method creates a cache object and returns a new function that takes precedence over the cached result. When the new function is executed for the first time, the return value of the function is placed in the cache

export function cached<F: Function> (fn: F): F { const cache = Object.create(null) return (function cachedFn (str: String) {const hit = cache[STR] // If hit has a value, it hit the cache. Otherwise, fn is called and the result is cached. Fn is below 3.1.2 genStaticKeys return hit | | (cache (STR) = fn (STR))} : any)}Copy the code

3.1.2 genStaticKeys

Methods location: SRC/compiler/optimizer. Js – > genStaticKeys

Method parameters:

  1. keysBy:.separatedkeyComposed of strings

The keys () method returns a function that verifies whether the key is in the map after calling makeMap.

function genStaticKeys (keys: string) :Function {
  return makeMap(
    'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' +
    (keys ? ', ' + keys : ' '))}Copy the code

3.1.2 makeMap

SRC /shared/util.js -> functions makeMap

Method parameters:

  1. str, separated by,keyComposed of strings
  2. expectLowerCase, whether lowercase

The STR () method returns a function to check whether a key is in the map. The STR () method returns a function to check whether a key is in the map.

export function makeMap ( str: string, expectsLowerCase? : boolean ): (key: string) => true | void { const map = Object.create(null) const list: Array<string> = str.split(',') for (let i = 0; i < list.length; i++) { map[list[i]] = true } return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val] }Copy the code

3.2 markStatic

Methods location: SRC/compiler/optimizer. Js – > functions provides markStatic

Method parameters: node, AST node object

Methods:

  1. Set the static property of node to the value returned by the isStatic(node) method.

  2. In this process, children of node are traversed, so each child in children is recursively processed. If child is a non-static node, node itself cannot be counted as a static node.

  3. In addition, if Node. ifConditions exist, then Node has v-if/ V-else/V-else instructions and recursively processes blocks in each condition statement. If blocks are not static elements, then Node is not static either. The node stack = false

  4. The recursion terminates if the node is not a platform reserved tag && not a slot tag && not an inline template; In other words, what can be statically tagged? Is the platform reserved tag or slot tag or inline template

function markStatic (node: ASTNode) {
  node.static = isStatic(node)
  if (node.type === 1) {
    // Do not set the component's slot contents to static nodes to avoid:
    // 1. Components cannot change slot nodes
    // 2. Static slot content fails when hot reloaded
  
    if(! isPlatformReservedTag(node.tag) && node.tag ! = ='slot' &&
      node.attrsMap['inline-template'] = =null
    ) {
      // Recursive terminating condition:
    
      return
    }

    // Iterate over the child nodes, recursively calling markStatic to mark the child node static properties
    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 static
      if(! child.static) { node.static =false}}// If node.ifConditons exist, the node has v-if/ V-else -if/ V-else directives
    Node.ifcondtions [].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

3.2.1 isStatic

Methods location: SRC/compiler/optimizer. Js – > function isStatic

Method parameters: node, AST node object

Function: Determine whether the node object is static. The criteria are as follows:

  1. node.type === 2Is the expression, is dynamic; And by the way,node.type2Do you remember where it was created? Yes, it isparseMethod resolutionhtmlCalled when the template parses to textoptions.charsMethod,options.charsWill determine if there is one in the text{{}}This syntax, if any, is creatednode.type2astNode;
  2. node.type === 3Is text and is static
  3. Comprehensive judgment conditions, meet one of the following conditions:
    • 3.1 inpreIn the tag, that is<pre></pre>The parcel;
    • 3.2 The following conditions are true:
      • 3.2.1 el.hasBindingsDon’t fortrue
      • 3.2.2 el.ifThere is no
      • 3.2.3 node.forThere is no
      • 3.2.4 notslotcompnentThese two built-in tags
      • 3.2.5 withv-fortemplateDirect child of
      • 3.2.6 nodeAll properties on are static properties

So when is true?

When prcessAttrs() is called, el.hansbindingds is set to true if the element has an instruction, which is a Vue instruction containing a shorthand such as /@.

function isStatic (node: ASTNode) :boolean {
  if (node.type === 2) { // expression
    return false
  }
  if (node.type === 3) { // text
    return true
  }
  return!!!!! (node.pre || ( ! node.hasBindings &&// No dynamic binding! node.if && ! node.for &&// No v-for, V-if/V-else -if/v-else! isBuiltInTag(node.tag) &&// Not a built-in slot or component tag
    isPlatformReservedTag(node.tag) && // Is a platform reserved label, i.e., not a custom component! isDirectChildOfTemplateFor(node) &&// Is not a direct child of template with v-for
    Object.keys(node).every(isStaticKey) // Each property on node is a static property))}Copy the code

3.3 markStaticRoots

Methods location: SRC/compiler/optimizer. Js – > markStaticRoots

Method parameters:

  1. node.astThe node object

The static root () method further marks the static root node. For a node to become a static root node, it must satisfy:

  1. The first is the element node, i.enode.type === 1;
  2. The element must be static, i.enode.statick === type
  3. It has to have child elements, which arenode.children.length
  4. Elements cannot have only one text node

Why are there so many requirements? For a node to qualify as a static root, it should have children that are not just static text. Otherwise the cost of hoisting out will outweigh the benefits and it’s better off to just always render it fresh. Because otherwise, it is much more expensive to raise the static root of these node bits than to render them directly with each update

function markStaticRoots (node: ASTNode, isInFor: boolean) {
  if (node.type === 1) {
    if (node.static || node.once) {
      // The node is static or has a V-once directive marking Node. StaticInFor = true or false
      node.staticInFor = isInFor
    }
   
    if(node.static && node.children.length && ! ( node.children.length ===1 &&
      node.children[0].type === 3
    )) {
      // The node itself is static and has children, which are not just text nodes
      node.staticRoot = true
      return
    } else {
      // Otherwise mark it as a non-static root
      node.staticRoot = false
    }

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

Four,

This article describes the process of statically marking the AST obtained by Parse. The point of this process is that the AST nodes are marked as static and will not be re-rendered when the data is updated. The core implementation is in the Optimize method:

  1. callgenStaticKeysCachedTo obtainisStaticKeysMethod standby;
  2. callmarkStaticMethod recursive processingastNodes and their children and conditional render nodes, set for each nodestaticProperty with a value ofisStatic()Method return value,isStaticThe method is based onastDetermine whether the information on the node object is static;
  3. callmarkStaticRoot()Check whether the node is a static root. The static root node will be ignored during data update and will not bepatch