前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >「.vue文件的编译」4. 模板编译之AST的优化

「.vue文件的编译」4. 模板编译之AST的优化

作者头像
tinyant
发布2023-02-24 10:31:36
3100
发布2023-02-24 10:31:36
举报

本节主要参考这里

代码语言:javascript
复制
/**
 * 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)
}

markStatic

代码语言:javascript
复制
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
    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) {
        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
        }
      }
    }
  }
}

如果这个节点是一个普通元素,则遍历它的所有 children,递归执行 markStatic。因为所有的 elseif 和 else 节点都不在 children 中, 如果节点的 ifConditions 不为空,则遍历 ifConditions 拿到所有条件中的 block,也就是它们对应的 AST 节点,递归执行 markStatic。在这些递归过程中,一旦子节点有不是 static 的情况,则它的父节点的 static 均变成 false。

isStatic

isStatic 是对一个 AST 元素节点是否是静态的判断,

代码语言:javascript
复制
function isStatic (node: ASTNode): boolean {
  if (node.type === 2) { // expression
    return false
  }
  if (node.type === 3) { // text
    return true
  }
  return !!(node.pre || (
    !node.hasBindings &amp;&amp; // no dynamic bindings
    !node.if &amp;&amp; !node.for &amp;&amp; // not v-if or v-for or v-else
    !isBuiltInTag(node.tag) &amp;&amp; // not a built-in
    isPlatformReservedTag(node.tag) &amp;&amp; // not a component
    !isDirectChildOfTemplateFor(node) &amp;&amp;
    Object.keys(node).every(isStaticKey)
  ))
}
  1. 如果是表达式,就是非静态
  2. 如果是纯文本,就是静态
  3. 对于一个普通元素
    • 如果有 pre 属性,那么它使用了 v-pre 指令,是静态,
    • 否则要同时满足以下条件:
      • 没有动态绑定。hasBindings是在上一小节中的processAttrs中判断的,使用正则/^v-|^@|^:|^.|^#/验证
      • 没有使用 v-ifv-for
      • 不是内置标签:slot,component
      • 非内置组件,是平台保留的标签,
      • 非带有 v-for 的 template 标签的直接子节点,
      • 节点的所有属性的 key 都满足静态 key (使用isStaticKey来判断) ``` // staticKeys: staticStyle、staticClass 在上一小节也会被添加 isStaticKey = genStaticKeysCached(options.staticKeys || '')

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

markStaticRoots

代码语言:javascript
复制
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.
    if (node.static &amp;&amp; node.children.length &amp;&amp; !(
      node.children.length === 1 &amp;&amp;
      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 第二个参数是 isInFor,对于已经是 static 的节点或者是 v-once 指令的节点,node.staticInFor = isInFor。 接着就是对于 staticRoot 的判断逻辑,从注释中我们可以看到,对于有资格成为 staticRoot 的节点,除了本身是一个静态节点外,必须满足拥有 children,并且 children 不能只是一个文本节点,不然的话把它标记成静态根节点的收益就很小了。

接下来和标记静态节点的逻辑一样,遍历 children 以及 ifConditions,递归执行 markStaticRoots

总结

深度遍历这个 AST 树,去检测它的每一棵子树是不是静态节点,如果是静态节点则它们生成 DOM 永远不需要改变,这对运行时对模板的更新起到极大的优化作用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • markStatic
    • isStatic
    • markStaticRoots
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档