Vue.js入門 ―最速で作るシンプルなWebアプリケーション

第2回Vue.js基礎文法最速マスター

第2回では、Vue.jsの基礎文法について、駆け足で紹介します。

Vue.jsは、ビューの領域にフォーカスしたシンプルなライブラリです。そのため、他のライブラリとの連携や、既存のプロジェクトへの導入も容易です。今回の内容を通して、自身のプロジェクトにVue.jsを導入するきっかけにしてください。

記事の内容は、Vue.jsがロードされたjsfiddleにアクセスして、コードを入力することで試せます。詳しい文法について知りたい場合は、公式のガイドAPIリファレンスを参照してください。

Vueインスタンスとデータバインディング

Vue.jsで基本になるのは、Vueコンストラクタから生成されるインスタンスです。

var vm = new Vue({
  el: '#example', // document.getElementById('example'), $('#example')[0] も可
  data: {
    name: 'みかん',
    price: 100
  }
});

// jsfiddle の環境で Console から vm を参照できるようにするため
window.vm = vm;

これは、MVVMパターンでいうViewModelに相当するもので、ViewとModelを仲立ちし状態や振る舞いを持っています。既存のプロジェクトでは、elプロパティにCSSセレクター、DOM要素のオブジェクトを指定することで手軽に始めることができます。

Vueインスタンスの持つ状態は、HTMLで{{ }}を使ったMustache記法で参照すると、Viewに反映されます。

<div id="example">
  <p>{{ name }} は {{ price }}円</p>
</div>

コードを実行したら、 priceの値がViewに反映されていることがわかるでしょう。Vueインスタンスの状態の変更は、データバインディング機構によって変更が検知され、すぐにViewに反映されます。

試しにGoogle Chromeの開発者ツールのConsoleタブで以下のコードを実行してみましょう。jsfiddleでは、入力されたコードはframe内で実行されています。なおコードを実行する前に、Consoleタブでコンテキストに「result (fiddle.jshell.net)」を指定してください。コードを実行すると、反映されることが確認できるはずです。

vm.price = 200;

また、HTMLには、以下のように単一のJavaScript式を記述することもできます。

  <p>{{ name }} は {{ price * 1.08 }}円</p>

算出プロパティ

上のようにMustache構文の中に式を記述してもよいですが、同じ式が複数の箇所に記述したり、複雑な式を記述するとメンテナンスが困難になります。このようなケースでは、算出プロパティという「Vueインスタンスの状態から、派生した値を計算してゲッターとして公開する機能」で解決できます。

具体例を見てみましょう。以下のようにcomputedプロパティに、参照されるプロパティと、消費税込みの価格を公開する関数の組のオブジェクトを指定します。

var vm = new Vue({
  // ...
  computed: {
    priceWithTax: function() {
      return this.price * 1.08;
    }
  }
});

すると、Vueインスタンスからプロパティとして、次のように参照できます。

vm.priceWithTax

HTMLからも参照できます。

  <p>{{ name }} は {{ priceWithTax }}円</p>

priceの値が変われば、再計算が必要になりますが、これはVue.jsが自動的におこないます。Vue.js側で状態の依存関係を把握して、必要なときだけ再計算をおこなうので、パフォーマンスの観点で心配も必要ありません。

ここまでのサンプルコードは、このURLから確認できます。

ディレクティブ

ディレクティブは、"v-"から始まるVue.jsで利用できる特別なHTMLの属性です。属性値の変化に応じたDOM操作や、DOMイベントのハンドリングを実現します。Vue.jsが標準で提供しているディレクティブの他に、自身でディレクティブを定義することも可能です。以降では、v-for, v-ifといったよく利用されるディレクティブについて取り上げます。

リストレンダリング

Vue.jsでは、v-forというディレクティブを利用して、配列のデータをリストレンダリングすることができます。

var vm = new Vue({
  el: '#example',
  data: {
    items: [
      {
        name: 'みかん',
        price: 100
      },
      {
        name: 'もも',
        price: 300
      },
      {
        name: 'いちじく',
        price: 500
      },
      {
        name: 'メロン',
        price: 1000
      }
    ]
  }
});
<div id="example">
  <li v-for="item in items">
    <p>{{ item.name }} は {{ item.price }}円</p>
  </li>
</div>

Vue.jsに限らず、他のビューを扱うライブラリ・フレームワークで話題になるのが、リストレンダリングのパフォーマンスです。リストのデータが変更されるたびに、リスト全体のDOM操作、レンダリングがおこなわれると表示のちらつきが発生して、ユーザフレンドリーとはいえません。各ライブラリ・フレームワークでは、必要なDOM操作だけおこなうように工夫しています。

Vue.jsでは、配列を操作するメソッドをラップして変更を検知したり、配列自体の置き換えが発生した場合もいくつかのヒューリスティックの実装によって最大限DOM要素を再利用しようとします。また、ユニークキーを指定する属性、v-bind:keyでリストのアイテムを識別可能にし、効率の良いDOM操作を実現します。

以下のコードでは、商品の名前がユニークと仮定してキーとしています。実際のAPIのレスポンスにはアイテムを識別する何かしらユニークな値が含まれているので、それを利用することが多いでしょう。

  <li v-for="item in items" v-bind:key="item.name">

リストレンダリングのサンプルコードは、このURLから確認できます。

条件付きレンダリング

表示・非表示を切り替えたい場合には、v-showディクレティブ、v-ifディレクィブを使います。属性値の式を評価して真とみなせる場合には要素を表示し、偽とみなせる場合には要素を非表示にします。

よくあるケースとしては、以下のようなバリデーションのエラーメッセージの表示があります。以下の例では、名前が一文字も入力されていない状態で式(!isValid)が真として評価され、エラーメッセージが表示されます。

var vm = new Vue({
  el: '#example',
  data: {
    name: '',
  },
  computed: {
    isValid: function () {
      return this.name.length > 0;
    }
  }
});
window.vm = vm;
<div id="example">
  <p>名前 {{ name }}</p>
  <p v-show="!isValid">
    名前を入力してください
  </p>
</div>

後ほどUIから入力を受けつける方法を紹介しますが、Consoleから値を変更することで、要素の表示・非表示を確かめてみましょう。開発者ツールのConsoleタブのコンテキストが「result (fiddle.jshell.net)」となっているか確認して、以下のようにnameの値を変更します。

エラーメッセージが表示されなくなることを確認できます。

vm.name = 'John';

v-if, v-showいずれも式の結果に応じて、表示・非表示を切り替えることができますが、それをどう実現しているかが異なります。

v-ifは式の結果に応じてDOM要素を追加・削除するのに対して、v-showはスタイルのdisplayプロパティの値を変更することで実現します。使い分けの基準としては、切り替えの頻度と初期表示のコストです。一般的にスタイルの操作よりもDOMの操作のほうがレンダリングのコストが高いので、頻繁に式の評価結果が変わる場合にはv-showを使うべきです。

一方、評価結果が偽から真へ一度しか変わらないようなケース、たとえば、ページ表示時にログイン状態か確認してからデータを取得して表示するような場合を想定してみましょう。この場合、初期表示時はDOM要素を生成せずレンダリングのコストを抑え、必要になったらDOM要素を生成するのが理想です。このような場合には、v-ifを使うとよいでしょう。

条件付きレンダリングのサンプルコードは、このURLから確認できます。

クラスとスタイルのバインディング

アプリケーションを開発していて、特定の条件が成立する場合に、見た目を変えたいケースがあるはずです。たとえば、先ほどの例でいえば、バリデーションエラーの場合はフォームを赤く囲んで表示したくなりませんか。このようなときに使えるのが、v-bind:class, v-bind:styleディレクティブです。

v-bind:classディレクティブは、属性値にオブジェクトを指定した場合に、値が真であるプロパティ名をclass属性値として反映します。先ほどの例のHTMLにv-bind:classディレクティブを適用してみましょう。この場合、isValidがfalseの場合はclass属性値が"error"になり、"error"というクラス名が指定されていることが確認できるはずです。

また、複数のクラス名を扱いたい場合は、オブジェクトではなくクラス名の文字列が格納された配列をv-bind:classディレクティブの属性値に指定することもできます。この場合は、配列を返す算出プロパティを用意するとよいでしょう。

<div id="example" v-bind:class="{'error': !isValid}">
  <p>名前 {{ name }}</p>
  <p v-show="!isValid">
    名前を入力してください
  </p>
</div>

v-bind:styleディレクティブでは、属性値のオブジェクトのプロパティがスタイルのプロパティと対応して、インラインスタイルとして反映されます。以下のように記述した場合、isValidがfalseの場合のstyle属性値は"border: 1px solid red;"となることが確認できるはずです。

<div id="example" v-bind:style="{'border': (isValid ? '' : '1px solid red')}">
  <p>名前 {{ name }}</p>
  <p v-show="!isValid">
    名前を入力してください
  </p>
</div>

v-if, v-showと同様にConsoleタブで、nameプロパティの値を変更して、表示が切り替わることを確認してみてください。スタイルのバインディングのサンプルコードは、このURLから確認できます。

メソッドとイベントハンドリング

v-onディレクティブを利用することで、DOMイベントをハンドリングして、JavaScriptのコードを実行することができます。

これまでの例を改良して、UIから入力を受け付けるようにしてみましょう。名前を表示するタグとエラーメッセージを表示するタグの間に以下の内容を追加します。

  <p>
    <input type="text" v-on:input="name = $event.target.value">
  </p>

v-onディレクティブの後ろにはイベント名が続きます。上の例はinputイベントをハンドリングして、入力の度にnameプロパティを変更しています。もちろん他のイベントを指定することもできます。入力の度に変更するのではなくテキストボックスがフォーカスされ、入力作業が完了したタイミングで変更をおこなう場合には、inputイベントの代わりにchangeイベントを利用します。

    <input type="text" v-on:change="name = $event.target.value">

属性値は、JavaScript式になっており、直接nameプロパティを変更しています。$eventは、Vue.jsが提供しているDOMイベントのオブジェクトへの参照です。

Mustache記法の場合と同様に、同じ式を複数の箇所に記述したり複雑な式を記述する場合には、JavaScript式ではなくメソッドを定義してそれを呼びだすこともできます。メソッドは、Vueインスタンスを生成する際のmethodsオプションで定義します。

var vm = new Vue({
  // ...
  methods: {
    updateName: function(event) {
      this.name = event.target.value;
    }
  }
  // ...
});
  <input type="text" v-on:input="updateName($event)">

もうひとつ、入力された値をサーバに送信すると仮定して、v-onディレクティブを利用してみましょう。エラーメッセージを表示する要素の下に以下の内容を追加してください。

  <p v-show="isValid">
    <button v-on:click="sendData">送信</button>
  </p>

また、methodsプロパティでsendData関数の定義を行ってください。

var vm = new Vue({
  // ...
  methods: {
    updateName: function(event) {
      this.name = event.target.value;
    },
    sendData: function() {
      alert(this.name);
    }
  }
  // ...
});

ボタンをクリックすることで、sendData関数が実行されていることがわかります。今回は、サーバのプログラムを用意していないのでalertで出力するだけですが、本来はXMLHttpRequestやFetchを利用してサーバと通信をおこないます。メソッドとイベントハンドリングのサンプルコードは、このURLで確認できます。

フォーム入力バインディング

先の例では、UIから入力を受け付けて状態を変更するためにイベントハンドリングとメソッドを利用しましたが、v-modelディレクティブを利用すればこれを簡潔に実現できます。これは双方向データバインディングを実現するディレクティブで、View(DOM)で変更があった場合にはViewModelの状態として反映します。逆にViewModelの状態の変更があった場合にはView(DOM)の値として反映します。

先ほどの例を、v-modelディレクティブで実現する場合には、inputタグを以下のように変更します。updateNameメソッドの定義は消してしまって構いません。

<input v-model="name">

どうでしょう。v-on:inputと同じ動きが実現できています。もしinputイベントではなくchangeイベントと同様の動きを実現する場合には、v-modelディレクティブの挙動を変更する修飾子(Modifier)という仕組みを利用します。今回は、lazyという修飾子を利用します。

<input v-model.lazy="name">

今回は紹介できませんでしたが、修飾子はv-onディレクティブにも存在し、DOMイベントを中断したりキー入力を制限するものがあります。v-modelの他の修飾子も併せてガイドAPIリファレンス を参照してみてください。

フォーム入力バインディングのサンプルコードは、このURLで確認できます。

まとめ

いかがでしたか。DOM APIやjQueryを利用するよりも、UIの実装がシンプル・簡潔にできると感じていただけたでしょうか。Vue.jsのテンプレートはHTMLベースで宣言的なので、独自のシンタックスを覚える必要がなく、とりかかりやすいです。直感的に読み書きを行うことができるのも特徴です。既存のプロジェクトへの導入も容易なので、ぜひ、ご自身のプロジェクトで、ちょっとしたUIの実装にVue.jsを活用してみてください。

次回は、Vue.jsのコンポーネントの機能について紹介します。お楽しみに。

おすすめ記事

記事・ニュース一覧