资讯专栏INFORMATION COLUMN

解析Vue编译器optimize源码

3403771864 / 380人阅读

  之前我们讲过template转成AST(抽象语法树),现在我就继续对模型树优化,进行静态标注。这要如何实现?

  在源码的注释中我们找到了下面这段话:

  /** * 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. */

  永远不需要变化的DOM就是静态的。

  重新渲染时,作为常量,无需创建新节点;

  optimize 源码之旅

  function optimize(root, options) { if (!root) { return } isStaticKey = genStaticKeysCached(options.staticKeys || ''); isPlatformReservedTag = options.isReservedTag || no; // first pass: mark all non-static nodes. markStatic$1(root); // second pass: mark static roots. markStaticRoots(root, false); }

  现在我们看到源码并不复杂初始定义了两个变量。

  isStaticKey 获取 genStaticKeysCached函数返回值, 获取makeMap(点此查看) 函数返回值引用 。

  isPlatformReservedTag 获取编译器选项isReservedTag的引用,检查给定的字符是否是保留的标签。

  接下来就是两个重要的方法 markStatic$1 标注静态节点、markStaticRoots 标注静态根节点,我们先来看下 markStatic$1的源码。

  markStatic$1源码

  function markStatic$1(node) { 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 (var i = 0, l = node.children.length; i < l; i++) { var child = node.children[i]; markStatic$1(child); if (!child.static) { node.static = false; } } if (node.ifConditions) { for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { var block = node.ifConditions[i$1].block; markStatic$1(block); if (!block.static) { node.static = false; } } } } }

  第一步我们要对节点做出判断并标注。

  node.static = isStatic(node);

  在这给元素描述对象(AST) 扩展了static属性,通过isStatic方法调用后返回值,这样就可以区分出静态和动态。

  isStatic源码

  function isStatic(node) { if (node.type === 2) { // expression return false } if (node.type === 3) { // text return true } return !!(node.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) )) }

  判断node.type的值为2,那返回的就是false,node.type的值为3,表示为静态文本返回 true 总结:节点类型为表达式,标注为非静态;普通文本为静态。

  上面的很容易理解

  复杂点的

  return !!(node.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) ))

  节点类型为表达式,标注为非静态;普通文本为静态。

  无动态绑定

  没有 v-if 和 v-for 指令

  不是内置的标签

  是平台保留标签(html和svg标签)

  不是 template 标签的直接子元素并且没有包含在 for 循环中

  结点包含的属性只能有isStaticKey中指定的几个

  现在你知道了 node.static=isStatic(node) 什么情况为false, 什么情况为true吧!

  回归到markStatic$1

  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 (var i = 0, l = node.children.length; i < l; i++) { var child = node.children[i]; markStatic$1(child); if (!child.static) { node.static = false; } } if (node.ifConditions) { for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) { var block = node.ifConditions[i$1].block; markStatic$1(block); if (!block.static) { node.static = false; } } } }

  来看看它做了什么,通过一个 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 }

  当遇到了非平台保留标签 isPlatformReservedTag(node.tag), 并且标签节点是 slot,并且节点中有inline-template(内联模板)三者都满足此时会终止函数的执行。

  如果不满足条件:

  for (var i = 0, l = node.children.length; i &lt; l; i++) { var child = node.children[i]; markStatic$1(child); if (!child.static) { node.static = false; } }

  通过 node.children 找到子节点,递归子节点。

  if (!child.static) { node.static = false; }

  当子节点非静态,那么该节点也标注非静态 。其实这就里面还需要优化,在之后的Vue3.0中增加了"动静分离的策略" 尤大称之为 Block tree 可以继续与大家沟通。

  接下来看下 markStaticRoots。

  markStaticRoots 源码

  function markStaticRoots(node, isInFor) { if (node.type === 1) { if (node.static || node.once) { node.staticInFor = isInFor; } //一个节点要成为根节点,那么要满足以下条件: //1、静态节点,并且有子节点 //2、子节点不能仅为一个文本节点 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 (var i = 0, l = node.children.length; i &lt; l; i++) { markStaticRoots(node.children[i], isInFor || !!node.for); } } if (node.ifConditions) { for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 &lt; l$1; i$1++) { markStaticRoots(node.ifConditions[i$1].block, isInFor); } } } }

  一个节点要成为静态根节点,需要满足以下条件:

  自身为静态节点,并且有子节点

  子节点不能仅为一个文本节点

  要是到第二个条件,这样就会让标记静态根节点的受益较小。可以以此递归循环其子节点,循环标记。


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/127975.html

相关文章

  • JS每日一题:简述一下Vue.js的template编译过程?

    摘要:问简述一下的编译过程先上一张图大致看一下整个流程从上图中我们可以看到是从后开始进行中整体逻辑分为三个部分解析器将模板字符串转换成优化器对进行静态节点标记,主要用来做虚拟的渲染优化代码生成器使用生成函数代码字符串开始前先解释一下抽象 20190215问 简述一下Vue.js的template编译过程? 先上一张图大致看一下整个流程showImg(https://image-static....

    NicolasHe 评论0 收藏0
  • Vue译器AST抽象语法树源码分析

     直接进入核心现在说说baseCompile核心代码:  //`createCompilerCreator`allowscreatingcompilersthatusealternative   //parser/optimizer/codegen,e.gtheSSRoptimizingcompiler.   //Herewejustexportadefaultcompilerusingthede...

    3403771864 评论0 收藏0
  • Vue原理】Compile - 源码版 之 optimize 标记静态节点

    摘要:一旦我们检测到这些子树,我们可以把它们变成常数,这样我们就不需要了在每次重新渲染时为它们创建新的节点在修补过程中完全跳过它们。否则,吊装费用将会增加好处大于好处,最好总是保持新鲜。 写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,...

    Soarkey 评论0 收藏0
  • Vue原理】Compile - 源码版 之 从新建实例到 compile结束的主要流程

    摘要:页面这个实例,按理就需要解析两次,但是有缓存之后就不会理清思路也就是说,其实内核就是不过是经过了两波包装的第一波包装在中的内部函数中内部函数的作用是合并公共和自定义,但是相关代码已经省略,另一个就是执行第二波包装在中,目的是进行缓存 写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 ...

    CODING 评论0 收藏0
  • vue原理Compile之optimize标记静态节点源码示例

      引言  optimize的内容虽然不多,但十分重要,它是一个更新性能优化,现在来说说:  首先找到optimize位置,就在 parse 处理完之后,generate 之前  varast=parse(template.trim(),options);   if(options.optimize!==false){   optimize(ast,options);   }   varcode=...

    3403771864 评论0 收藏0

发表评论

0条评论

3403771864

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<