Skip to content

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, beforeUnmountel.style.displaynone にしたり "" にしたりしているだけです.

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}

何と!これで全部です!