上篇文章说到了模板解析的第一步parse
,现在来说第二步optimize
,用于优化静态内容的渲染,主要是给静态节点打上一些标记。
Vue
中对于生成的AST
会做优化,静态内容是指和数据没有关系,不需要每次都刷新的内容,这一步主要就是找出 ast 中的静态内容,并加以标注。
这一步的代码比parse
要少太多,应该压力会小很多 🙄,先看一下入口代码:
1 | /** |
可以看到代码非常简短,只有两步:markStatic
和markStaticRoots
。我们挨个把这里的每个子函数讲一下。
genStaticKeysCached、isStaticKey
genStaticKeysCached
用于缓存一个函数的执行结果,这种技巧在很多地方有可以用到,比如求解斐波那契数列。
1 | const genStaticKeysCached = cached(genStaticKeys); // 缓存genStaticKeys结果,每次先从缓存中查找,找不到再执行genStaticKeys |
所以经过一系列嵌套,我们的isStaticKey
就是查找指定key
是否存在于makeMap
的结果中,如果之前已经查找过这个key
那么可以直接从cache
中拿到缓存的结果。
markStatic
这个函数会遍历整个 AST,为了更好的了解其中的过程,最好进行断点调试每一步,比如如下的template
:
1 | <div id="app"> |
生成的AST
会是:
1 | { |
现在看看函数定义,它的目的是给 AST 上每个节点打上static
标记。
1 | function markStatic(node: ASTNode) { |
思路是先利用isStatic
判断自身是否是static
的,然后判断所有children
,只要其中一个child
不是static
的,那么自己也不是static
的;最后如果处于v-if、v-else-if、v-else
,则只要其中一个分支不是static
的,整个node
就设置为不是static
。
isStatic
判断一个 AST 节点是否为静态的,上面也提到静态内容是指和数据没有关系,不需要每次都刷新的内容。
1 | function isStatic(node: ASTNode): boolean { |
可以看到标记static
的条件有 2 个:
- 节点上有
v-pre
指令,官网文档也说了在编译时遇到这个指令会跳过它 - 没有动态绑定属性 && 不是
v-if
&& 不是v-for
&& 不是内置的标签slot
、component
&& 是平台保留标签,即HTML
或SVG
标签 && 不是位于一个<template v-for="xxx">
&& 节点的所有属性均是type,tag,attrsList,attrsMap,plain,parent,children,attrs
其中之一。
第二个条件非常严格,想成为static
的还真是不容易。。。
经过这一步之后,我们的AST
变为
1 | { |
markStaticRoots
用来找到那种本身是static
的,同时只有唯一的一个text
子节点,将他们标记为staticRoot
,即静态根节点。
1 | function markStaticRoots(node: ASTNode, isInFor: boolean) { |
最终咱们optimize
过的AST
如下:
1 | { |