上篇文章说到了模板解析的第一步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 | { |