画面の表示制御はディレクティブを使おう!

ディレクティブとは HTML 要素を動的に変更するために使用します。
ディレクティブを利用することで、Vue.js で定義したデータを HTML に出力することができるようになります。

Vue.js は標準でいくつかのディレクティブを提供しており、独自のディレクティブも作成することができます。

ここでは標準で Vue.js に搭載されている、デフォルトディレクティブを紹介します。

デフォルトディレクティブ一覧

Vue.js がデフォルトで用意しているディレクティブに以下のようなものがあります。

ディレクティブ名解説
v-text要素に対してテキストを挿入するために使用します。
v-html要素に対して HTML を挿入するために使用します。
v-show条件に応じて要素の表示・非表示を切り替えます。
v-if と違い、要素は常に存在しており、CSS の display プロパティを切り替えを行います。
v-if条件に応じて要素を表示・非表示にします。例えば、v-if=“show” とすることで、show 変数が true の場合に要素が表示され、false の場合には非表示にします。
v-elsev-if ディレクティブとセットで使用し、v-if の条件が false の場合に表示します。
v-else-ifv-if ディレクティブとセットで使用し、複数の条件の中から最初に true となるものを表示します。
v-for配列やオブジェクトから要素を動的に生成します。
例えば、v-for=“item in items” とすることで、items 変数に格納されている配列から要素を生成します。
v-on要素に対してイベントリスナーを追加します。
例えば、v-on:click=“doSomething” とすることで、クリックイベントを doSomething 関数にバインドします。
v-bind要素のプロパティを動的にバインドします。
例えば、v-bind:href=“url” とすることで、url 変数の値を href 属性にバインドします。
v-model要素の値と Vue インスタンスのデータを双方向バインドします。例えば、v-model=“message” とすることで、 要素の値と message 変数が双方向バインドします。
v-slotコンポーネントの内部に任意のコンテンツを挿入します。
v-preバインディングやディレクティブなどを無視します。
v-cloak要素に対して Vue.js のデータバインディングが適用される前に、スタイルの表示を隠すことができます。
このディレクティブを利用することで、一時的に見た目が崩れることを防ぐことができます。
v-once要素に対するデータバインディングが一度だけ実行され、その後は更新されなくなります。
v-memoこれは、コンポーネント内で使用される静的な要素を記憶し、変更がない限り再レンダリングしないようにすることができる機能です。
これにより、コンポーネントのパフォーマンスを改善することができます。
v-is3.1.0 では非推奨です。代わりに  vue:  プレフィックス付きの  is  属性  を使ってください。

テキストレンダリング(v-text)

<html>
  <head>
    <title>テキストレンダリング</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div>
        <div style="text-align: center">
          <h1 v-text="message"></h1>
        </div>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: "Hello World",
      };
    },
  }).mount("#app");
</script>

v-text ではタグ要素内にテキストの出力を行うことができます。

v-text は、v-html と異なり、HTML タグをそのまま出力することはできません。そのため、v-text を使用することで、XSS (Cross-Site Scripting) 脆弱性を回避することができます。

HTML レンダリング(v-html)

<html>
  <head>
    <title>innnerHTMLレンダリング</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div>
        <div style="text-align: center" v-html="message"></div>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: "<h1>Hello World</h1>",
      };
    },
  }).mount("#app");
</script>

要素に対して HTML を挿入するために使用します。

HTML そのものを挿入できますが、挿入する HTML 文字列が信頼できることが前提となります。不正な HTML を挿入することで、XSS (Cross-Site Scripting) 脆弱性が発生する可能性があります。

条件付き表示(v-show)

<html>
  <head>
    <title>条件付きディスプレイ</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div style="text-align: center">
        <button v-on:click="reverseFlg">Click Me</button>
        <h2 v-show="showFlg">{{ message }}</h2>
      </div>
    </body>
  </div>
</html>
<script type="text/javascript">
  const { createApp } = Vue;
  createApp({
    el: "#intro",
    data() {
      return {
        message: "Hello World",
        showFlg: true,
      };
    },
    methods: {
      reverseFlg: function () {
        this.showFlg = !this.showFlg;
      },
    },
  }).mount("#app");
</script>

条件付きレンダリング(v-if)とは違い、要素が常に DOM の保持されます。表示と非常時の切り替えを css によって行います。

切り替えを CSS で行うため、表示の切り替えのコストが低いです。 頻繁に切り替えを行う場合は、条件付きレンダリング(v-if)ではなく条件付き表示(v-show)を使用しましょう。

条件付きレンダリング(v-if)

<html>
  <head>
    <title>条件付きレンダリング</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div style="text-align: center">
        <button v-on:click="reverseFlg">Click Me</button>
        <h1 v-if="showFlg === 1">{{ message1 }}</h1>
        <h1 v-else-if="showFlg === 2">{{ message2 }}</h1>
        <h1 v-else>{{ message3 }}</h1>
      </div>
    </body>
  </div>
</html>
<script type="text/javascript">
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message1: "Hello World",
        message2: "Hello",
        message3: "World",
        showFlg: 0,
      };
    },
    methods: {
      reverseFlg: function () {
        this.showFlg = ++this.showFlg % 3;
      },
    },
  }).mount("#app");
</script>

条件付きレンダリングでは、if、if-else、if-else-if、show 等の Vue.js が提供している ディレクティブ を使用して、条件が満たされた場合にのみ出力することができます。

  • if 条件が true の場合出力します。

  • if-else 条件 false の場合出力します。

  • if-else-if if 条件が false の場合別の条件を提示します。

リストレンダリング(v-for)

<html>
  <head>
    <title>リストレンダリング</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div id="intro">
        <h1>三大フレームワーク</h1>
        <ul>
          <li v-for="flamework in flameworks">{{ flamework }}</li>
        </ul>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        flameworks: \["vue", "react", "angular"\],
      };
    },
  }).mount("#app");
</script>

リストレンダリングは配列などの複雑なデータの表示制御をしたい時など便利です。

v-forflameworks配列を指定し、要素の数繰り返します。 繰り返した時に要素は変数flameworkに格納されます。

イベントリスナー(v-on)

<html>
  <head>
    <title>イベントリスナー</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div>
        <div style="text-align: center">
          <h1 v-text="count"></h1>
          <button v-on:click="countUp">カウントアップ</button>
        </div>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        count: 0,
      };
    },
    methods: {
      countUp: function () {
        this.count++;
      },
    },
  }).mount("#app");
</script>

v-on を使用することでイベントリスナーを登録することができます。

v-on:click と記載していますが、これは省略することができ :click を書くことで同等の機能を使うことができます。

データバインディング(v-bind)

<html>
  <head>
    <title>データバインド</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div style="text-align: center">
        <a v-bind:href="url" target="\_blank">Googleへ</a><br />
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        url: "http://www.google.com",
      };
    },
  }).mount("#app");
</script>

HTML 要素のプロパティを Vue.js インスタンスのデータに バインド することができます。 バインドとは、HTML 要素の プロパティ値を Vue.js のデータで動的に更新することを言います。

v-bind のショートハンドとして、:も使用できます。

サンプルコードでは、href属性に Vue インスタンスに定義したurlをバインドしています。

双方向バインディング(v-model)

<html>
  <head>
    <title>双方向データバインディング</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div style="text-align: center">
        <input v-model="message" />
        <p>{{ message }}</p>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: "Hello World",
      };
    },
  }).mount("#app");
</script>

Vue.js には双方向データバインディングという機能が備わっています。

これは単方向データバインディングの処理の複雑さ、実装の冗長さを改善してくれる機能です。

単方向データバインディングと双方向データバインディングについての違いは以下の通りです。

  • 単方向データバインディング

    • vue.js のデータの変更が  view  に自動的に反映される

    • view  の変更をデータに反映させるには、イベントハンドラを利用し、データの更新が必要

  • 双方向データバインディング

    • vue.js のデータの変更が  View  に自動的に反映される

    • view  の変更が vue.js のデータに自動的に反映される

要はユーザーに見える部分の view が変更された場合に実際の Vue.js のデータをイベントハンドラなどを使用せずに自動的に更新をかけてくれます。

テンプレートインサート(v-slot)

コンポーネントの内容をカスタマイズすることができます。

バインディングスキップ(v-pre)

<html>
  <head>
    <title>バインディングスキップ</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div style="text-align: center">
        <h1 v-pre>{{ message }}</h1>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: "Hello World",
      };
    },
  }).mount("#app");
</script>

特定の要素とその子要素に対して Vue.js のコンパイラがコンパイルをスキップするよう指示します。 つまり、その要素に対して、Vue.js のバインディングやディレクティブなどが無視されます。

Vue.js による更新が不要な要素に使用され、描画が高速になります。

バインディングクローク(v-cloak)

<html>
  <head>
    <title>バインディングクローク</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div>
        <div style="text-align: center">
          <h1 v-cloak v-text="message"></h1>
        </div>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: "Hello World",
      };
    },
  }).mount("#app");
</script>

Vue.js のディレクティブで、要素に対して Vue.js のデータバインディングが適用される前に、 HTML 要素を非表示にできます。

Vue.js は、データバインディングを適用する前に、要素に対して適用されるスタイルの適用を待機します。この仕様はデータバインディングが完了していない要素に対して、一瞬だけデザインが崩れる場合が想定されます。 それを防ぐためには v-cloakを使用します。 v-cloakはデータバインディングが適用されると、自動的に非表示になっていた HTML 要素を表示します。

ワンスバインディング(v-once)

<html>
  <head>
    <title>ワンスバインディング</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <div id="app">
    <body>
      <div style="text-align: center">
        <input v-model="message" />
        <p v-once>{{ message }}</p>
      </div>
    </body>
  </div>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: "Hello World",
      };
    },
  }).mount("#app");
</script>

要素に対するデータバインディングが一度だけ実行され、その後は更新されなくなります。

Vue.js は、データの変更に対して、要素を自動的に更新しますが、v-onceを使用することで、それを抑制できます。

メモレンダリング(v-memo)

<!DOCTYPE html>
<html>
  <head>
    <title>メモバインディング</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <div style="text-align: center">
        <ul>
          <li v-for="memo in memos" v-memo="\[\]">{{ memo.name }}</li>
        </ul>
        <button @click="addMemos">追加 memos</button>
      </div>
    </div>
  </body>
</html>
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        memos: \[
          { id: 1, name: "memo 1" },
          { id: 2, name: "memo 2" },
          { id: 3, name: "memo 3" },
        \],
      };
    },
    methods: {
      addMemos() {
        for (let i = 4; i <= 10; i++) {
          this.memos.push({ id: i, name: \`memo ${i}\` });
        }
      },
    },
  }).mount("#app");
</script>

コンポーネント内で使用される静的な要素を記憶し、変更がない限り再レンダリングしないようにすることができる機能です。 これにより、コンポーネントのパフォーマンスを改善することができます。

大量のデータを表示するリストコンポーネント:リストコンポーネントは、多くのデータを表示するために使用されますが、データが変更されない限り、リスト要素を再レンダリングする必要はありません。 v-memo を使用することで、データが変更されない限り、リスト要素を再レンダリングしないようにすることができます。これによりコンポーネントのパフォーマンスを向上します。

終わりに

Vue.js のディレクティブ機能を利用することで、開発者がより簡単に、かつ効率的にコンポーネントを作成することができます。 特に v-bind や v-on などの基本的なディレクティブは、要素のバインディングやイベントハンドリングを簡単に実装することができるため、開発効率が上がります。 また、v-if や v-for などの条件分岐や繰り返し処理を行うディレクティブも、見やすく、かつ容易に実装することができるため、コードのメンテナンス性も高くなります。 その上、v-memo などのパフォーマンス向上を目的としたディレクティブもあり、アプリケーションのパフォーマンスを改善することもできます。

総じて、Vue.js のディレクティブ機能は、開発効率を上げ、かつコードのメンテナンス性を高めるために役立つので、どんどん使っていきましょう。

https://github.com/wiblok/Vue.js