The v-bind Directive
Now, let's keep reading and progressing.
Consider a component like the following.
<script setup>
import { ref } from "vue";
const dynamicData = ref("a");
</script>
<template>
<div :data-dynamic="dynamicData">Hello, World!</div>
</template>
Compilation Result and Overview
The compilation result is as follows. (We're getting accustomed to this, so the explanations are becoming a bit rough (lol)).
const _sfc_main = {
vapor: true,
__name: "App",
setup(__props, { expose: __expose }) {
__expose();
const dynamicData = ref("a");
const __returned__ = { dynamicData, ref };
Object.defineProperty(__returned__, "__isScriptSetup", {
enumerable: false,
value: true,
});
return __returned__;
},
};
import {
renderEffect as _renderEffect,
setDynamicProp as _setDynamicProp,
template as _template,
} from "vue/vapor";
const t0 = _template("<div>Hello, World!</div>");
function _sfc_render(_ctx) {
const n0 = t0();
_renderEffect(() => _setDynamicProp(n0, "data-dynamic", _ctx.dynamicData));
return n0;
}
In particular,
function _sfc_render(_ctx) {
const n0 = t0();
_renderEffect(() => _setDynamicProp(n0, "data-dynamic", _ctx.dynamicData));
return n0;
}
is the _setDynamicProp
part.
As expected, you can now predict the implementation method.
In summary, it's an effect that sets the _ctx.dynamicData
to the data-dynamic
attribute of n0
, which is immediately understandable.
Reading the Compiler
Familiar route: transformElement
-> buildProps
-> transformProps
-> directiveTransform
-> transformVBind
.
packages/compiler-vapor/src/transforms/vBind.ts
...Or is it?
Actually, this only handles shorthand and truly transforms v-bind
, without registering effects and such.
In fact, regarding this, it is directly implemented in transformElement
's buildProps
.
The implementation is around here.
236 context.registerEffect(
237 [prop.exp],
238
239 {
240 type: IRNodeTypes.SET_DYNAMIC_EVENTS,
241 element: context.reference(),
242 event: prop.exp,
243 },
244 )
A bit above, there is also handling for when v-bind
does not have an arg
(e.g., v-bind="obj"
).
208 if (prop.type === NodeTypes.DIRECTIVE && !prop.arg) {
209 if (prop.name === 'bind') {
210 // v-bind="obj"
211 if (prop.exp) {
212 dynamicExpr.push(prop.exp)
213 pushMergeArg()
214 dynamicArgs.push({
215 kind: IRDynamicPropsKind.EXPRESSION,
216 value: prop.exp,
217 })
218 } else {
Anyway, since we were able to see where SET_DYNAMIC_EVENTS
is registered, it's okay.
Let's also read the Codegen as it is.
33export function genOperation(
34 oper: OperationNode,
35 context: CodegenContext,
36): CodeFragment[] {
40 case IRNodeTypes.SET_DYNAMIC_PROPS:
41 return genDynamicProps(oper, context)
63export function genDynamicProps(
64 oper: SetDynamicPropsIRNode,
65 context: CodegenContext,
66): CodeFragment[] {
67 const { vaporHelper } = context
68 return [
69 NEWLINE,
70 ...genCall(
71 vaporHelper('setDynamicProps'),
72 `n${oper.element}`,
73 ...oper.props.map(
74 props =>
75 Array.isArray(props)
76 ? genLiteralObjectProps(props, context) // static and dynamic arg props
77 : props.kind === IRDynamicPropsKind.ATTRIBUTE
78 ? genLiteralObjectProps([props], context) // dynamic arg props
79 : genExpression(props.value, context), // v-bind=""
80 ),
81 ),
82 ]
83}
There shouldn't have been any particularly difficult parts.
Reading the Runtime
There's almost nothing to read here as well.
When the key
is "class"
or "style"
, it just does a bit of formatting.
112export function setDynamicProp(el: Element, key: string, value: any): void {
113 // TODO
114 const isSVG = false
115 if (key === 'class') {
116 setClass(el, value)
117 } else if (key === 'style') {
118 setStyle(el as HTMLElement, value)
119 } else if (isOn(key)) {
120 on(el, key[2].toLowerCase() + key.slice(3), () => value, { effect: true })
121 } else if (
122 key[0] === '.'
123 ? ((key = key.slice(1)), true)
124 : key[0] === '^'
125 ? ((key = key.slice(1)), false)
126 : shouldSetAsProp(el, key, value, isSVG)
127 ) {
128 setDOMProp(el, key, value)
129 } else {
130 // TODO special case for <input v-model type="checkbox">
131 setAttr(el, key, value)
132 }
133}
22export function setClass(el: Element, value: any): void {
23 const prev = recordPropMetadata(el, 'class', (value = normalizeClass(value)))
24 if (value !== prev && (value || prev)) {
25 el.className = value
26 }
27}
12export function setStyle(el: HTMLElement, value: any): void {
13 const prev = recordPropMetadata(el, 'style', (value = normalizeStyle(value)))
14 patchStyle(el, prev, value)
15}