The v-html Directive
Consider a component like the following.
vue
<script setup>
import { ref } from "vue";
const inner = ref("<p>Hello, v-html</p>");
</script>
<template>
<div v-html="inner" />
</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 inner = ref("<p>Hello, v-html</p>");
const __returned__ = { inner, ref };
Object.defineProperty(__returned__, "__isScriptSetup", {
enumerable: false,
value: true,
});
return __returned__;
},
};
import {
renderEffect as _renderEffect,
setHtml as _setHtml,
template as _template,
} from "vue/vapor";
const t0 = _template("<div></div>");
function _sfc_render(_ctx) {
const n0 = t0();
_renderEffect(() => _setHtml(n0, _ctx.inner));
return n0;
}
A new helper called setHtml
has appeared.
Its implementation does not change in particular.
Reading the Compiler
We will follow the path transformElement
-> buildProps
-> transformProps
-> directiveTransform
-> transformVHtml
.
It's very simple, so I'll include the entire text.
1import { IRNodeTypes } from '../ir'
2import type { DirectiveTransform } from '../transform'
3import { DOMErrorCodes, createDOMCompilerError } from '@vue/compiler-dom'
4import { EMPTY_EXPRESSION } from './utils'
5
6export const transformVHtml: DirectiveTransform = (dir, node, context) => {
7 let { exp, loc } = dir
8 if (!exp) {
9 context.options.onError(
10 createDOMCompilerError(DOMErrorCodes.X_V_HTML_NO_EXPRESSION, loc),
11 )
12 exp = EMPTY_EXPRESSION
13 }
14 if (node.children.length) {
15 context.options.onError(
16 createDOMCompilerError(DOMErrorCodes.X_V_HTML_WITH_CHILDREN, loc),
17 )
18 context.childrenTemplate.length = 0
19 }
20
21 context.registerEffect([exp], {
22 type: IRNodeTypes.SET_HTML,
23 element: context.reference(),
24 value: exp,
25 })
26}
It simply registers an effect with SET_HTML
.
It performs processing similar to when transformText
is called.
Let's look at Codegen.
33export function genOperation(
34 oper: OperationNode,
35 context: CodegenContext,
36): CodeFragment[] {
↓
48 case IRNodeTypes.SET_HTML:
49 return genSetHtml(oper, context)
↓
6export function genSetHtml(
7 oper: SetHtmlIRNode,
8 context: CodegenContext,
9): CodeFragment[] {
10 const { vaporHelper } = context
11 return [
12 NEWLINE,
13 ...genCall(
14 vaporHelper('setHtml'),
15 `n${oper.element}`,
16 genExpression(oper.value, context),
17 ),
18 ]
19}
It's familiar.
Reading the Runtime
Let's just read setHtml
.
196export function setHtml(el: Element, value: any): void {
197 const oldVal = recordPropMetadata(el, 'innerHTML', value)
198 if (value !== oldVal) {
199 el.innerHTML = value
200 }
201}
It's very simple and just sets innerHTML
.
For the time being, it seems to extract the old value from the meta information to prevent unnecessary sets.