还记得 compile 中传入的第一个 nodeTransform 是那个吗? 没错,第一个就是 transformOnce,transform 是把 getBaseTransformPreset 生成的 nodeTransforms 放到最前面,然后才是 compiler-dom 传进来的 nodeTransforms。所以接下来,我们就先讲讲 vOnce Transform。

export function getBaseTransformPreset(
  prefixIdentifiers?: boolean
): TransformPreset {
  return [
    [
      transformOnce,
      transformIf,
      transformFor,
      ...(!__BROWSER__ && prefixIdentifiers
        ? [
            // order is important
            trackVForSlotScopes,
            transformExpression
          ]
        : []),
      transformSlotOutlet,
      transformElement,
      trackSlotScopes,
      transformText
    ],
    {
      on: transformOn,
      bind: transformBind,
      model: transformModel
    }
  ]
}
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(
    prefixIdentifiers
)

transform(ast, {
    ...options,
    prefixIdentifiers,
    nodeTransforms: [
      ...nodeTransforms,
      ...(options.nodeTransforms || []) // user transforms
    ],
    directiveTransforms: {
      ...directiveTransforms,
      ...(options.directiveTransforms || {}) // user transforms
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

我们现在来看看 vOnce,vOnce 要做的事情很简单,对于 type 是 NodeTypes.ELEMENT,且上面有 v-once 指令的,首先把需要的运行时方法添加通过 context.helper 添加进 helpers,然后什么都不做,对的,只返回 exitfn,因为 v-once 需要做的就是缓存其他 transform 对节点处理,需要放在最前面,这样他的 exitfn 才可以是最后执行,这样 node.codegenNode 就能通过 context.cache 缓存起来。

export const transformOnce: NodeTransform = (node, context) => {
  if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
    context.helper(SET_BLOCK_TRACKING)
    return () => {
      if (node.codegenNode) {
        node.codegenNode = context.cache(node.codegenNode, true /* isVNode */)
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10