1. 本际云推荐 - 专业推荐VPS、服务器,IDC点评首页
  2. 云主机运维
  3. VPS运维

解析Vue编译器optimize源码

模板转为抽象语法树(AST)

作为云服务器推荐网的小编,我们之前已经讲过将模板转为抽象语法树(AST),现在我们来接着讲述如何对模型树进行优化,实现静态标注。

解析Vue编译器optimize源码

优化的目标和实现方法

在源码的注释中我们找到了如下描述:

目标:遍历生成的模板抽象语法树并检测纯静态的子树,即永远不需要变化的DOM部分。一旦检测到这些子树,我们可以:

  1. 将其提升为常量,这样我们就不需要在每次重渲染时创建新的节点;
  2. 完全跳过它们的渲染过程。

根据注释,我们可以知道要实现静态标注,需要检测出永远不需要变化的DOM。在重新渲染时,由于已被标记为常量,无需再创建新节点。具体实现方法如下:

  • 标记静态节点和静态根节点。
  • 将静态节点提升为常量,防止每次重新渲染时创建新节点。

源码分析

现在我们开始分析源码。

function optimize(root, options) {
  if (!root) return;
  isStaticKey = genStaticKeysCached(options.staticKeys || '');
  isPlatformReservedTag = options.isReservedTag || no;

  // 第一次标记:标记所有非静态节点。
  markStatic(root);

  // 第二次标记:标记静态根节点。
  markStaticRoots(root, false);
}

代码看起来很简单,定义了两个变量isStaticKey和isPlatformReservedTag。其中isStaticKey获取genStaticKeysCached函数返回的值,这里的genStaticKeysCached在编译阶段生成了一个函数返回值,并通过makeMap(点此查看)函数返回引用。isPlatformReservedTag获取编译器选项中的isReservedTag,然后检查给定的字符是否是保留的标签。

标记静态节点

接下来我们来看看markStatic方法的实现。

function markStatic(node) {
  node.static = isStatic(node);

  // 如果节点为元素节点,递归遍历子节点。
  if (node.type === 1) {
    // 如果元素为非保留标签、不是 、不存在内联模板,则退出循环,标记非静态。
    if (!isPlatformReservedTag(node.tag) && node.tag !== 'slot' && !node.attrsMap['inline-template']) {
      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;
      }
    }

    // 针对具有v-if指令的节点进行遍历,若存在非静态节点,则该元素标记为非静态节点。
    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;
        }
      }
    }
  }
}

首先,对节点做出判断并标注node.static属性值。这里的isStatic方法会判断节点是否是静态,如果是则返回true,否则返回false。然后,对所有元素节点递归遍历它的子节点,如果存在非静态子节点,则该元素标记为非静态节点。

接着,如果该元素有v-if指令,也会进行类似的遍历,并将其标记为非静态。

标记静态根节点

接下来我们来看看markStaticRoots方法的实现。

function markStaticRoots(node, isInFor) {
  if (node.type === 1) {
    if (node.static || node.once) {
      node.staticInFor = isInFor;
    }

    // 如果该节点为静态节点并有子节点,则它是一个静态根节点。
    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));
      }
    }

    // 如果该节点使用v-if指令,则递归遍历。
    if (node.ifConditions) {
      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
        markStaticRoots(node.ifConditions[i].block, isInFor);
      }
    }
  }
}

如果一个节点符合以下两个条件,它就是静态根节点:

  • 自身是静态节点
  • 自身有子节点,或者其子节点不是文本节点

如果符合,将标记该节点的staticRoot属性值为true。如果一个节点被标记为静态根节点,则其子节点无论是否是静态都被视为静态节点。

接下来,如果该节点的子节点是一个列表,则递归遍历该列表,如果使用了v-if指令,则递归遍历v-if的block。

总结

以上是如何进行静态标注的源码分析,标记静态节点和静态根节点有助于缓存页面并提高访问速度。希望这篇文章可以帮助您更好地理解Vue的工作原理,以及如何优化Vue的性能。

原创文章,作者:小编小本本,如若转载,请注明出处:https://www.benjiyun.com/yunzhujiyunwei/vps-yunwei/7394.html