The v-once Directive
Consider a component like the following.
vue
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<p v-once>{{ count }}</p>
</template>Compilation Result and Overview
The compilation result is as follows.
js
const _sfc_main = {
vapor: true,
__name: "App",
setup(__props, { expose: __expose }) {
__expose();
const count = ref(0);
const __returned__ = { count, ref };
Object.defineProperty(__returned__, "__isScriptSetup", {
enumerable: false,
value: true,
});
return __returned__;
},
};
import { setText as _setText, template as _template } from "vue/vapor";
const t0 = _template("<p></p>");
function _sfc_render(_ctx) {
const n0 = t0();
_setText(n0, _ctx.count);
return n0;
}What stands out is that the setText part is not wrapped in renderEffect.
Since v-once is rendered only once, there is no need to wrap it in renderEffect.
Reading the Compiler
We will follow transformElement -> buildProps -> transformProps -> directiveTransform -> transformVOnce.
It's very simple, so I'll include the entire text.
1import { NodeTypes, findDir } from '@vue/compiler-dom'
2import type { NodeTransform } from '../transform'
3
4export const transformVOnce: NodeTransform = (node, context) => {
5 if (
6 // !context.inSSR &&
7 node.type === NodeTypes.ELEMENT &&
8 findDir(node, 'once', true)
9 ) {
10 context.inVOnce = true
11 }
12}It simply enables the inVOnce flag that context holds.
When inVOnce is true, it calls registerOperation with registerEffect and finishes, meaning no effect is generated.
137 registerEffect(
138 expressions: SimpleExpressionNode[],
139 ...operations: OperationNode[]
140 ): void {
141 expressions = expressions.filter(exp => !isConstantExpression(exp))
142 if (this.inVOnce || expressions.length === 0) {
143 return this.registerOperation(...operations)
144 }Since there is nothing particularly to read in the runtime, this concludes it for now.
