Skip to content

Latest commit

 

History

History
87 lines (77 loc) · 3.85 KB

8.md

File metadata and controls

87 lines (77 loc) · 3.85 KB

入口开始,解读Vue源码(七)—— $mount 内部实现 --- compile optimize标记节点

上篇文章,我们介绍了compile 的 parse部分,至此我们完成了对一个html字符串模板解析成一个AST语法树的过程。下一步就是我们需要通过optimize方法,将AST节点进行静态节点标记。为后面 patch 过程中对比新旧 VNode 树形结构做优化。被标记为 static 的节点在后面的 diff 算法中会被直接忽略,不做详细的比较。

optimize(ast, options)

接下来我们去简单分析一下optimize功能

export function optimize (root: ?ASTElement, options: CompilerOptions) {
  if (!root) return
  // staticKeys 是那些认为不会被更改的ast的属性
  isStaticKey = genStaticKeysCached(options.staticKeys || '')
  isPlatformReservedTag = options.isReservedTag || no
  // 第一步 标记 AST 所有静态节点
  markStatic(root)
  // 第二步 标记 AST 所有父节点(即子树根节点)
  markStaticRoots(root, false)
}

首先标记所有静态节点:

function isStatic (node: ASTNode): boolean {
  if (node.type === 2) { // 表达式
    return false
  }
  if (node.type === 3) { // 文本节点
    return true
  }
  // 处理特殊标记
  return !!(node.pre || ( // v-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)
  ))
}

ASTNode 的 type 字段用于标识节点的类型,可查看上一篇的 AST 节点定义:type 为 1 表示元素,type 为 2 表示插值表达式,type 为 3 表示普通文本。可以看到,在标记 ASTElement 时会依次检查所有子元素节点的静态标记,从而得出该元素是否为 static。上面 markStatic 函数使用的是树形数据结构的深度优先遍历算法,使用递归实现。 接下来继续标记静态树:

function markStaticRoots (node) {
   if (node.type === 1) {
       // 用以标记在v-for内的静态节点。这个属性用以告诉renderStatic(_m)对这个节点生成新的key,避免patch error
       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.
       // 一个节点如果想要成为静态根,它的子节点不能单纯只是静态文本。否则,把它单独提取出来还不如重渲染时总是更新它性能高。
       if (node.static && node.children.length && !(
               node.children.length === 1 &&
               node.children[0].type === 3
           )) {

           node.staticRoot = true
           return
       } else {
           node.staticRoot = false
       }

       for (let i = 0; i < node.children.length; i++) {
           markStaticRoots(node.children[i])
       }
   }
}

markStaticRoots 函数里并没有什么特别的地方,仅仅是对静态节点又做了一层筛选。

总结:

optimizer旨在为语法树的节点标上static和staticRoot属性。 遍历第一轮,标记static属性:

  1. 判断node是否为static(有诸多条件)
  2. 标记node的children是否为static,若存在non static子节点,父节点更改为static = false

遍历第二轮,标记staticRoot

  1. 标记static或节点为staticRoot,这个节点type === 1(一般是含有tag属性的节点)
  2. 具有v-once指令的节点同样被标记staticRoot
  3. 为了避免过度优化,只有static text为子节点的节点不被标记为staticRoot
  4. 标记节点children的staticRoot