v-show ディレクティブ
以下のようなコンポーネントを考えます.
vue
<script setup>
import { ref } from "vue";
const flag = ref("");
</script>
<template>
<p v-show="flag">Hello, v-show!</p>
</template>
コンパイル結果と概要
コンパイル結果は以下のようになります.
js
const _sfc_main = {
vapor: true,
__name: "App",
setup(__props, { expose: __expose }) {
__expose();
const flag = ref("");
const __returned__ = { flag, ref };
Object.defineProperty(__returned__, "__isScriptSetup", {
enumerable: false,
value: true,
});
return __returned__;
},
};
import {
vShow as _vShow,
withDirectives as _withDirectives,
template as _template,
} from "vue/vapor";
const t0 = _template("<p>Hello, v-show!</p>");
function _sfc_render(_ctx) {
const n0 = t0();
_withDirectives(n0, [[_vShow, () => _ctx.flag]]);
return n0;
}
注目するべきは _withDirectives(n0, [[_vShow, () => _ctx.flag]]);
の部分です.
前回に引き続き withDirectives
関数を使っていますが,今回は runtimeDirective として vShow
関数を使っています.
コンパイラを読む
transformElement
-> buildProps
-> transformProps
-> directiveTransform
-> transformVShow
と辿っていきます.
非常にシンプルなので全文載せてしまいます.
1import { DOMErrorCodes, createDOMCompilerError } from '@vue/compiler-dom'
2import type { DirectiveTransform } from '../transform'
3import { IRNodeTypes } from '../ir'
4
5export const transformVShow: DirectiveTransform = (dir, node, context) => {
6 const { exp, loc } = dir
7 if (!exp) {
8 context.options.onError(
9 createDOMCompilerError(DOMErrorCodes.X_V_SHOW_NO_EXPRESSION, loc),
10 )
11 }
12
13 context.registerOperation({
14 type: IRNodeTypes.WITH_DIRECTIVE,
15 element: context.reference(),
16 dir,
17 name: 'vShow',
18 builtin: true,
19 })
20}
name: 'vShow'
で WITH_DIRECTIVE
を登録しているだけです.
ランタイムを読む
こちらもシンプルなので全文載せてしまいます.
beforeMount
, updated
, beforeUnmount
で el.style.display
を none
にしたり ""
にしたりしているだけです.
1import type { ObjectDirective } from '../directives'
2
3const vShowMap = new WeakMap<HTMLElement, string>()
4
5export const vShow: ObjectDirective<HTMLElement> = {
6 beforeMount(node, { value }) {
7 vShowMap.set(node, node.style.display === 'none' ? '' : node.style.display)
8 setDisplay(node, value)
9 },
10
11 updated(node, { value, oldValue }) {
12 if (!value === !oldValue) return
13 setDisplay(node, value)
14 },
15
16 beforeUnmount(node, { value }) {
17 setDisplay(node, value)
18 },
19}
20
21function setDisplay(el: HTMLElement, value: unknown): void {
22 el.style.display = value ? vShowMap.get(el)! : 'none'
23}
何と!これで全部です!