text: /* @flow */
  
  import { makeMap, isBuiltInTag, cached, no } from 'shared/util'
  
  let isStaticKey
  let isPlatformReservedTag
  
  const genStaticKeysCached = cached(genStaticKeys)
  
  /**
   * Goal of the optimizer: walk the generated template AST tree
   * and detect sub-trees that are purely static, i.e. parts of
   * the DOM that never needs to change.
   *
   * Once we detect these sub-trees, we can:
   *
   * 1. Hoist them into constants, so that we no longer need to
   *    create fresh nodes for them on each re-render;
   * 2. Completely skip them in the patching process.
   */
  export function optimize (root: ?ASTElement, options: CompilerOptions) {
    if (!root) return
    isStaticKey = genStaticKeysCached(options.staticKeys || '')
    isPlatformReservedTag = options.isReservedTag || no
    // first pass: mark all non-static nodes.
    markStatic(root)
    // second pass: mark static roots.
    markStaticRoots(root, false)
  }
  
  function genStaticKeys (keys: string): Function {
    return makeMap(
      'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' +
      (keys ? ',' + keys : '')
    )
  }
  
  function markStatic (node: ASTNode) {
    node.static = isStatic(node)
    if (node.type === 1) {
      // do not make component slot content static. this avoids
      // 1. components not able to mutate slot nodes
      // 2. static slot content fails for hot-reloading
      // 是组件，不是slot,没有inline-template
      if (
        !isPlatformReservedTag(node.tag) &&
        node.tag !== 'slot' &&
        node.attrsMap['inline-template'] == null
      ) {
        return
      }
      for (let i = 0, l = node.children.length; i < l; i++) {
        const child = node.children[i]
        // 标记静态
        markStatic(child)
        if (!child.static) {
          // 如果有一个child 不是 static, 当前 node 不是 static
          node.static = false
        }
      }
      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
          }
        }
      }
    }
  }
  
  function markStaticRoots (node: ASTNode, isInFor: boolean) {
    if (node.type === 1) {
      if (node.static || node.once) {
        node.staticInFor = isInFor
      }
      // 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.
      // 如果一个元素内只有文本节点，此时这个元素不是静态的Root
      // Vue 认为这种优化会带来负面的影响
      if (node.static && node.children.length && !(
        node.children.length === 1 &&
        node.children[0].type === 3
      )) {
        node.staticRoot = true
        return
      } else {
        node.staticRoot = false
      }
      // 检测当前节点的子节点中是否有静态的Root
      if (node.children) {
        for (let i = 0, l = node.children.length; i < l; i++) {
          markStaticRoots(node.children[i], isInFor || !!node.for)
        }
      }
      if (node.ifConditions) {
        for (let i = 1, l = node.ifConditions.length; i < l; i++) {
          markStaticRoots(node.ifConditions[i].block, isInFor)
        }
      }
    }
  }
  
  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 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)
    ))
  }
  
  function isDirectChildOfTemplateFor (node: ASTElement): boolean {
    while (node.parent) {
      node = node.parent
      if (node.tag !== 'template') {
        return false
      }
      if (node.for) {
        return true
      }
    }
    return false
  }
  