浅析Vue源码(五)—— $mount中template的编译--optimize

上篇文章浅析Vue源码(四)—— $mount中template的编译--parse,咱们介绍了compile 的 parse部分,至此咱们完成了对一个html字符串模板解析成一个AST语法树的过程。下一步就是咱们须要经过optimize方法,将AST节点进行静态节点标记。为后面 patch 过程当中对比新旧 VNode 树形结构作优化。被标记为 static 的节点在后面的 diff 算法中会被直接忽略,不作详细的比较。html

src/compiler/optimizer.js
复制代码
export function optimize (root: ?ASTElement, options: CompilerOptions) {
  if (!root) return
  // staticKeys 是那些认为不会被更改静态的ast的属性
  isStaticKey = genStaticKeysCached(options.staticKeys || '')
  isPlatformReservedTag = options.isReservedTag || no
  // first pass: mark all non-static nodes.
  // 第一步 标记 AST 全部静态节点
  markStatic(root)
  // second pass: mark static roots.
  // 第二步 标记 AST 全部父节点(即子树根节点)
  markStaticRoots(root, false)
}
复制代码

首先标记全部静态节点:vue

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 节点定义:node

type 为 1 表示元素,git

type 为 2 表示插值表达式,github

type 为 3 表示普通文本。算法

能够看到,在标记 ASTElement 时会依次检查全部子元素节点的静态标记,从而得出该元素是否为 static。上面 markStatic 函数使用的是树形数据结构的深度优先遍历算法,使用递归实现。 接下来继续标记静态树:bash

function markStaticRoots (node: ASTNode, isInFor: boolean) {
  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 } 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) } } } } 复制代码

markStaticRoots 函数里并无什么特别的地方,仅仅是对静态节点又作了一层筛选。数据结构

总结

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

判断node是否为static(有诸多条件) 标记node的children是否为static,若存在non static子节点,父节点更改成static = false 遍历第二轮,标记staticRootpost

标记static或节点为staticRoot,这个节点type === 1(通常是含有tag属性的节点) 具备v-once指令的节点一样被标记staticRoot 为了不过分优化,只有static text为子节点的节点不被标记为staticRoot 标记节点children的staticRoot

要是喜欢的话给我一个star,github

感谢muwoo提供的思路

相关文章
相关标签/搜索