Vue|Vue 编译器optimize源码分析

目录

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

引言 接上文 parseHTML 函数源码解析 chars、end、comment钩子函数
上一章节我们讲到通过解析将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-reloadingif (!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) { // expressionreturn false } if (node.type === 3) { // textreturn 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-inisPlatformReservedTag(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-inisPlatformReservedTag(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-reloadingif ( !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 < 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 & & node.children.length & & !(node.children.length === 1 & & 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 < l; i++) {markStaticRoots(node.children[i], isInFor || !!node.for); }}if (node.ifConditions) {for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {markStaticRoots(node.ifConditions[i$1].block, isInFor); }} }}

一个节点要成为静态根节点,需要满足以下条件:
  • 自身为静态节点,并且有子节点
  • 子节点不能仅为一个文本节点
对于第二个条件,主要考虑到标记静态根节点的受益较小。接下来递归循环其子节点,循环标记。
【Vue|Vue 编译器optimize源码分析】以上就是Vue 编译器optimize源码分析的详细内容,更多关于Vue 编译器optimize的资料请关注脚本之家其它相关文章!

    推荐阅读