[前端] Vue编译器optimize源码分析

2053 0
Honkers 2022-10-21 15:42:01 | 显示全部楼层 |阅读模式
目录

    引言optimize 源码之旅
      markStatic$1源码isStatic源码复杂点的回归到markStatic$1markStaticRoots 源码



引言

接上文 parseHTML 函数源码解析 chars、end、comment钩子函数
上一章节我们讲到通过解析将template转成AST(抽象语法树),接下来继续对模型树优化,进行静态标注。那么问题来了,什么是静态标注?为什么要静态标注。
在源码的注释中我们找到了下面这段话:
  1. /**
  2. * Goal of the optimizer: walk the generated template AST tree
  3. * and detect sub-trees that are purely static, i.e. parts of
  4. * the DOM that never needs to change.
  5. *
  6. * Once we detect these sub-trees, we can:
  7. *
  8. * 1. Hoist them into constants, so that we no longer need to
  9. *    create fresh nodes for them on each re-render;
  10. * 2. Completely skip them in the patching process.
  11. */
复制代码
    永远不需要变化的DOM就是静态的。重新渲染时,作为常量,无需创建新节点;

optimize 源码之旅
  1. function optimize(root, options) {
  2.         if (!root) {
  3.                 return
  4.         }
  5.         isStaticKey = genStaticKeysCached(options.staticKeys || '');
  6.         isPlatformReservedTag = options.isReservedTag || no;
  7.         // first pass: mark all non-static nodes.
  8.         markStatic$1(root);
  9.         // second pass: mark static roots.
  10.         markStaticRoots(root, false);
  11. }
复制代码
可以看到源码并不复杂初始定义了两个变量。
    isStaticKey 获取 genStaticKeysCached函数返回值, 获取 makeMap (点此查看) 函数返回值引用 。isPlatformReservedTag 获取编译器选项 isReservedTag 的引用,检查给定的字符是否是保留的标签。
接下来就是两个重要的方法 markStatic$1 标注静态节点、markStaticRoots 标注静态根节点,我们先来看下 markStatic$1的源码。

markStatic$1源码
  1. function markStatic$1(node) {
  2.         node.static = isStatic(node);
  3.         if (node.type === 1) {
  4.                 // do not make component slot content static. this avoids
  5.                 // 1. components not able to mutate slot nodes
  6.                 // 2. static slot content fails for hot-reloading
  7.                 if (
  8.                         !isPlatformReservedTag(node.tag) &&
  9.                         node.tag !== 'slot' &&
  10.                         node.attrsMap['inline-template'] == null
  11.                 ) {
  12.                         return
  13.                 }
  14.                 for (var i = 0, l = node.children.length; i < l; i++) {
  15.                         var child = node.children[i];
  16.                         markStatic$1(child);
  17.                         if (!child.static) {
  18.                                 node.static = false;
  19.                         }
  20.                 }
  21.                 if (node.ifConditions) {
  22.                         for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {
  23.                                 var block = node.ifConditions[i$1].block;
  24.                                 markStatic$1(block);
  25.                                 if (!block.static) {
  26.                                         node.static = false;
  27.                                 }
  28.                         }
  29.                 }
  30.         }
  31. }
复制代码
第一步判断节点状态并标注。
  1. node.static = isStatic(node);
复制代码
  1. 在这给元素描述对象(AST) 扩展了static属性,通过isStatic方法调用后返回值,确认哪些节点是静态的,哪些是动态的。
复制代码
isStatic源码
  1. function isStatic(node) {
  2.         if (node.type === 2) { // expression
  3.                 return false
  4.         }
  5.         if (node.type === 3) { // text
  6.                 return true
  7.         }
  8.         return !!(node.pre || (
  9.                 !node.hasBindings && // no dynamic bindings
  10.                 !node.if && !node.for && // not v-if or v-for or v-else
  11.                 !isBuiltInTag(node.tag) && // not a built-in
  12.                 isPlatformReservedTag(node.tag) && // not a component
  13.                 !isDirectChildOfTemplateFor(node) &&
  14.                 Object.keys(node).every(isStaticKey)
  15.         ))
  16. }
复制代码
在这判断node.type的值为2,表示为表达式返回false,node.type的值为3,表示为静态文本返回 true 总结:节点类型为表达式,标注为非静态;普通文本为静态。
上面的很容易理解

复杂点的
  1. return !!(node.pre || (
  2.                 !node.hasBindings && // no dynamic bindings
  3.                 !node.if && !node.for && // not v-if or v-for or v-else
  4.                 !isBuiltInTag(node.tag) && // not a built-in
  5.                 isPlatformReservedTag(node.tag) && // not a component
  6.                 !isDirectChildOfTemplateFor(node) &&
  7.                 Object.keys(node).every(isStaticKey)
  8. ))
复制代码
节点类型为表达式,标注为非静态;普通文本为静态。
    无动态绑定没有 v-if 和 v-for 指令不是内置的标签是平台保留标签(html和svg标签)不是 template 标签的直接子元素并且没有包含在 for 循环中结点包含的属性只能有isStaticKey中指定的几个
现在你知道了 node.static=isStatic(node) 什么情况为false, 什么情况为true吧!

回归到markStatic$1
  1. if (node.type === 1) {
  2.         // do not make component slot content static. this avoids
  3.         // 1. components not able to mutate slot nodes
  4.         // 2. static slot content fails for hot-reloading
  5.         if (
  6.                 !isPlatformReservedTag(node.tag) &&
  7.                 node.tag !== 'slot' &&
  8.                 node.attrsMap['inline-template'] == null
  9.         ) {
  10.                 return
  11.         }
  12.         for (var i = 0, l = node.children.length; i < l; i++) {
  13.                 var child = node.children[i];
  14.                 markStatic$1(child);
  15.                 if (!child.static) {
  16.                         node.static = false;
  17.                 }
  18.         }
  19.         if (node.ifConditions) {
  20.                 for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {
  21.                         var block = node.ifConditions[i$1].block;
  22.                         markStatic$1(block);
  23.                         if (!block.static) {
  24.                                 node.static = false;
  25.                         }
  26.                 }
  27.         }
  28. }
复制代码
来看看它做了什么,通过一个 if 判断node.type值为1,对标签节点进行处理。如果遇到特殊情况会直接退出去。 什么特殊情况呢?
  1. // do not make component slot content static. this avoids
  2. // 1. components not able to mutate slot nodes
  3. // 2. static slot content fails for hot-reloading
  4. if (
  5.         !isPlatformReservedTag(node.tag) &&
  6.         node.tag !== 'slot' &&
  7.         node.attrsMap['inline-template'] == null
  8. ) {
  9.         return
  10. }
复制代码
当遇到了非平台保留标签 isPlatformReservedTag(node.tag), 并且标签节点是 slot,并且节点中有inline-template(内联模板)三者都满足此时会终止函数的执行。
如果不满足条件:
  1. for (var i = 0, l = node.children.length; i < l; i++) {
  2.         var child = node.children[i];
  3.         markStatic$1(child);
  4.         if (!child.static) {
  5.                 node.static = false;
  6.         }
  7. }
复制代码
通过 node.children 找到子节点,递归子节点。
  1. if (!child.static) {
  2.      node.static = false;
  3. }
复制代码
子节点非静态,该节点也标注非静态 。这块设计的不太合理有更多好的优化方案,在Vue3.0中增加了"动静分离的策略" 尤大称之为 Block tree 后续在跟大家掰扯。
接下来看下 markStaticRoots。

markStaticRoots 源码
  1. function markStaticRoots(node, isInFor) {
  2.         if (node.type === 1) {
  3.                 if (node.static || node.once) {
  4.                         node.staticInFor = isInFor;
  5.                 }
  6.                 //一个节点要成为根节点,那么要满足以下条件:
  7.                 //1、静态节点,并且有子节点
  8.                 //2、子节点不能仅为一个文本节点
  9.                 if (node.static && node.children.length && !(
  10.                                 node.children.length === 1 &&
  11.                                 node.children[0].type === 3
  12.                         )) {
  13.                         node.staticRoot = true;
  14.                         return
  15.                 } else {
  16.                         node.staticRoot = false;
  17.                 }
  18.                 //循环递归标记
  19.                 if (node.children) {
  20.                         for (var i = 0, l = node.children.length; i < l; i++) {
  21.                                 markStaticRoots(node.children[i], isInFor || !!node.for);
  22.                         }
  23.                 }
  24.                 if (node.ifConditions) {
  25.                         for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {
  26.                                 markStaticRoots(node.ifConditions[i$1].block, isInFor);
  27.                         }
  28.                 }
  29.         }
  30. }
复制代码
一个节点要成为静态根节点,需要满足以下条件:
    自身为静态节点,并且有子节点子节点不能仅为一个文本节点
对于第二个条件,主要考虑到标记静态根节点的受益较小。接下来递归循环其子节点,循环标记。
以上就是Vue 编译器optimize源码分析的详细内容,更多关于Vue 编译器optimize的资料请关注中国红客联盟其它相关文章!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4010
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行