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

第6回Vue.jsの高度なアプリケーション開発

はじめに

これまでの連載で、Vue.jsの基礎文法やコンポーネントを学習し、Vue Routerを使ってシングルページアプリケーションの基本的な実装を行いました。連載最終回である今回は、より現場を意識した実践的なアプリケーションの開発について紹介します。

これまでの連載ではVue.jsの気軽に使えるメリットや本質的な機能を理解してもらうために、ES5のJavaScriptを使い、各種のコマンドラインツールなしで解説してきました。より実践向きな高度なアプリケーション開発になるため、ES2015以降のJavaScriptを使用し、エディタ、コマンドラインや開発を補助するライブラリなどの様々なツールを活用しながら学習していきます。

vue-cliによるアプリケーション開発

前準備

開発にnpm(Node.js)を利用します。Node.jsは日本語公式サイトを参考にインストールしてください。npmはNode.jsのパッケージマネージャで、Node.jsをインストールすると標準でインストールされます。Node.jsは執筆時点の最新安定バージョンのv6.9.2を使用します。

vue-cli

vue-cliとは、Vue.js向けのアプリケーション開発環境をセットアップする公式のコマンドラインツールです。

JavaScriptでアプリケーションを構築する場合、モジュール化、バンドラー/プリプロセッサによるビルド、JavaScriptの静的構文チェック(Lint⁠⁠、単体テストやE2Eテストなどが必要になります。これらを満たした開発環境のセットアップは長期的なメンテナンスと開発の生産性を考えると必須とも言えますが、自分で一から用意するのはかなりの手間で、徒労感の伴う作業となるでしょう。この面倒なアプリケーション開発環境のセットアップを担ってくれるのがvue-cilと呼ばれるコマンドラインツールです。これによりすぐにVue.jsアプリケーションの開発に着手できます。

vue-cliのインストール

では、さっそくvue-cliを使ってVue.jsのアプリケーション開発環境を整えてみましょう。まずは、npmでvue-cliをインストールします。

$ npm install -g vue-cli

インストールが完了したら、vueコマンドが使用できるようになります。試しに以下のvue-cliのバージョンを確認するコマンドを実行してみましょう。

$ vue --version
2.6.0

本稿では執筆時点での最新バージョンである2.6.0を使用します。

アプリケーションプロジェクトの作成

vue-cliのインストールが完了したら、テンプレートを利用してアプリケーション開発環境をセットアップします。Vue.jsは公式にVue.jsアプリケーションプロジェクトテンプレートをいくつか提供しています。最も人気のwebpackプロジェクトテンプレートを使用し、vue-cli-gihyo-exampleという名前でアプリケーションプロジェクトを作成することにします。

$ vue init webpack vue-cli-gihyo-example

上記コマンドを実行すると、コマンドライン上でプロジェクト名やLint、テストなどのセットアップの有無を対話的に問われます。今回は以下のように設定します(Authorの部分はコマンドライン実行環境によって変わります⁠⁠。

  This will install Vue 2.x version of template.
  
  For Vue 1.x use: vue init webpack#1.0 vue-cli-gihyo-example

? Project name vue-cli-gihyo-example
? Project description A Vue.js project
? Author kazuya kawaguchi <xxx@domain.com>
? Vue build standalone
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Setup unit tests with Karma + Mocha? Yes
? Setup e2e tests with Nightwatch? Yes

入力が完了すると下記の内容が出力され、vue-cli-gihyo-exampleというディレクトリにVue.jsアプリケーションプロジェクトがセットアップされます。

vue-cli · Generated "vue-cli-gihyo-example".

To get started:

 cd vue-cli-gihyo-example
 npm install
 npm run dev

Documentation can be found at https://vuejs-templates.github.io/webpack

出力されたメッセージには、開発を始めるために実行するべきコマンドと、webpackプロジェクトテンプレートの公式ドキュメントの参照先URLが出力されます。

アプリケーションの開発を開始する

Vue.jsアプリケーションプロジェクトのセットアップが完了したので、前節のvue-cliによるセットアップにおいて出力された内容に従って進めてみます。

$ cd vue-cli-gihyo-example
$ npm install
$ npm run dev

npm installでVue.jsアプリケーション開発に必要な依存モジュールのインストールが完了します。

次にnpm run devで動作確認のための開発用サーバが起動すると、ブラウザが起動し図1の内容が表示されるのを確認できるはずです。

図1 開発コマンド実行で表示された画面
図1 開発コマンド実行で表示された画面

最小限に動作するアプリケーションの雛形となるコードが生成されています。以降は、これをベースにして開発していきます。

単一ファイルコンポーネントによるモジュール化

vue-cliでVue.jsアプリケーションプロジェクトをセットアップし開発の準備ができました。

単一ファイルコンポーネントとは

早速実践として開発の紹介に入りたいところですが、ここではvue-cliを使った開発では理解の欠かせない単一ファイルコンポーネントについて紹介します。

vue-cliでセットアップしたVue.jsアプリケーションプロジェクトでは、コンポーネントのモジュール管理は単一ファイルコンポーネント(Single File Components)で行います。

単一ファイルコンポーネントとは、.vue拡張子ファイル内に定義したコンポーネントで、以下の例のような要素で構成されたHTMLベースのシンタックスのものです。

  • <template>タグ: コンポーネントにおいてUIのセマンティックな構造をテンプレートとして定義する要素です。HTMLの他、Mustache記法、v-ifなどのVue.jsで提供する文法がそのまま利用可能です。
  • <style>タグ: コンポーネントにおいてUIの見た目を制御する要素です。CSSを使用して定義することができます。
  • <script>タグ: コンポーネントにおいてUIの振る舞いを制御する要素です。JavaScriptを使用することができ、連載第3回で解説した同じ作法でコンポーネント定義が必要です。

単一ファイルコンポーネントの例を以下に示します。

<template>
  <p class="message">メッセージ: {{ msg }}</p>
</template>

<style>
.message { color: #42b983; }
</style>

<script>
export default {
  data () {
    return { msg: 'こんにちは!' }
  }
}
</script>

従来のWeb標準の技術構成(HTML, CSS, JavaScript)でコンポーネントを定義することができるので学習コストはほぼありません。単一ファイルコンポーネントはその名の通り、1つのファイルに1つしかコンポーネントを定義できません。

第3回で、Vue.component/componentsオプションによるUIをコンポーネント化する仕組みを紹介しましたが、この仕組みとexport/import(commonJSの場合はrequire)を使用することによりモジュール化は可能です。

しかしながら、単一ファイルコンポーネントは先の例の通り、要素を役割ごとに明確に区分してファイルごとに定義できるため、保守性と再利用性の高いコンポーネントの実装ができる点でより優れていると言えるでしょう。

vue-cliによりセットアップされたアプリケーションプロジェクトでは、標準で単一ファイルコンポーネントを利用するための環境がセットアップされています。使わない理由はありません。

単一ファイルコンポーネントの使用方法

単一ファイルコンポーネントは、バンドルツールとして、webpackならvue-loaderbrowserifyの場合はvueifyを使って最終的にVue.jsのVue.component/componentsオプションに登録可能なオブジェクトに変換します。このため、コンポーネントの利用方法は従来と変わりません。以下は、単一ファイルコンポーネントを使用する例です。

// 単一ファイルコンポーネントでモジュール化されたHello.vueをインポート
import Hello from './Hello' 

// インポートされた該当コンポーネントを使用対象となるVueコンポーネントに登録
new Vue({
  components: { Hello }
}).$mount('#app')
<div id="app">
  <hello></hello>
</div>

単一ファイルコンポーネントのその他機能

その他に、単一ファイルコンポーネントは以下のような開発をより効率化する機能を提供しています。

  • BabelによるES2015以降のJavaScript
  • スコープ付きCSS(Scoped CSS)
  • PostCSSによるCSSプロセッサ
  • CSS ModulesによるCSSのモジュール化
  • プリプロセッサ(Pre-Processor)による多言語(Pug/SaSS/CoffeeScriptなど)による単一ファイルコンポーネントの使用
  • ホットリロード(Hot Reload)による開発時のライブリロード

これらを使用することでさらに生産性と再利用性の高いコンポーネントのモジュール化が可能になります。詳細についてはドキュメント(vue-loader)を参照してください。

単一ファイルコンポーネントの作成

単一ファイルコンポーネントについて簡単ですが解説しました。vue-cliでセットアップしたVue.jsアプリケーションプロジェクトをベースに開発できる状態になったので、実際に単一ファイルコンポーネントを実装してみましょう。先ほど単一ファイルコンポーネントの解説で使用した単純なメッセージを表示するコンポーネントを少し改造して、以下のようなmsgプロパティ経由でメッセージが表示し、デフォルトメッセージを持ったHelloコンポーネントを実装します。

<template>
  <p class="message">メッセージ: {{ msg }}</p>
</template>

<style>
.message { color: #42b983; }
</style>

<script>
export default {
  props: {
    msg: {
      type: String,
      default: 'こんにちは!'
    }
  }
}
</script>

vue-cliによってHelloコンポーネントとしてセットアップされているので、このコンポーネントの実装内容を先の今回の実装のものに置き換えます。エディタでsrc/components/Hello.vueを開いて置き換えて保存してください。その結果、ブラウザには図2のような画面が出力されるかと思います。

図2 Helloコンポーネント1
図2 Helloコンポーネント1

'こんにちは!'というメッセージがVue.jsのロゴの下に表示されるのをブラウザで確認できましたでしょうか?表示内容が初期のものと変わっていることを確認できたと思います。これは、アプリケーションのコード変更を検知して更新するホットリロードと呼ばれるもので、本稿でセットアップしたwebpackアプリケーションプロジェクトの開発環境に含まれています。リロードの手間がなくなり、開発生産性を大きく高めてくれます。

さて、Helloコンポーネントはmsgプロパティに文字列値を設定するとその文字列値が表示される仕様になっているので、ここでmsgプロパティに文字列値を設定してみましょう。エディタで、src/App.vueを開いて内容をtemplateタグの実装内容を以下のように変更してください。

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <hello msg="ようこそ!"></hello>
  </div>
</template>

この結果、図3のように'ようこそ!'というメッセージがVue.jsのロゴの下に表示されるのを確認できるかと思います。

図3 Helloコンポーネント2
図3 Helloコンポーネント1

単一ファイルコンポーネント作成を体験することができました。初期にセットアップされているコンポーネントで単一ファイルコンポーネントを実装していきました。新規にコンポーネントを作成する場合は、アプリケーションプロジェクトとしてセットアップされたコードが管理されるsrcディレクトリ配下に、新規に.vueファイルを作成してコンポーネントを実装するとよいでしょう。

テスト

実際のアプリケーション開発においては単体テスト、E2Eテストをすることでアプリケーションが正しく動作しているかどうか検証を行います。単体テストとE2Eテストの環境が今回は既にセットアップされているのですぐに着手できます。Vue.jsにおけるコンポーネントの単体テストを試してみましょう。

$ npm run unit

このコマンドを実行すると、セットアップされたKarmawebpackおよびPhantomJSといったツールや実行環境によってtest/unit/配下にある単体テストコードが実行されテスト結果が出力されます。以下はその内容のテスト結果の部分抜粋です。

  Hello.vue
    ✗ should render correct contents
        null is not an object (evaluating 'vm.$el.querySelector('.hello h1').textContent')
        webpack:///test/unit/specs/Hello.spec.js:10:45 <- index.js:165:46

テストが失敗していることがわかります。これは、vue-cliによって初期セットアップされたHelloコンポーネントの単体テストの実行結果です。解説にあたってHelloコンポーネントの実装内容を変更したので、それにともなってテストが失敗しています。

ここで変更したHelloコンポーネントの動作内容を検証する単体テストを実装してみましょう。エディタでtest/unit/specs/Hello.spec.jsを開いて以下のように単体テストを実装します。

// Vue.js本体とHelloコンポーネントをインポートする
import Vue from 'vue'
import Hello from 'src/components/Hello'

describe('Helloコンポーネント', () => {
  it('デフォルトメッセージが正しく描画されていること', () => {
    const Ctor = Vue.extend(Hello)
    const vm = new Ctor().$mount()
    expect(vm.$el.textContent).to.equal('メッセージ: こんにちは!')
  })

  it('msgプロパティで指定した文字列値で正しく描画されていること', () => {
    const Ctor = Vue.extend(Hello)
    // 初期プロパティ値の変更は、propsData経由で行う
    // 公式ドキュメントを参照: https://jp.vuejs.org/v2/api/#propsData
    const vm = new Ctor({ propsData: { msg: 'ようこそ!' } }).$mount()
    expect(vm.$el.textContent).to.equal('メッセージ: ようこそ!')
  })

  it('親コンポーネント経由でmsgプロパティに指定された文字列で正しく描画されていること', done => {
    const vm = new Vue({
      data: { message: '' },
      components: { Hello },
      // render関数で実装されている内容は
      // templateオプションに'<hello :msg="message"></hello>'に指定するのと同義
      render (h) { return h('hello', { props: { msg: this.message } }) }
    }).$mount()
    // マウント後、コンポーネントの状態値(props/data)の変更に対するDOMの更新検証は、
    // 非同期に更新されるため、Vue.nextTickを使用する
    vm.message = 'ようこそ!'
    Vue.nextTick(() => {
      expect(vm.$el.textContent).to.equal('メッセージ: ようこそ!')
      done()
    })
  })
})

上記の単体テストコードは、本稿のvue-cliでセットアップされたMochaChaiという単体テスト向けのライブラリを使用しています。

いくつかDOMを操作するブラウザ環境に依存したコードが入っていますが、それ以外はNode.jsでも実行できるシンプルな単体テストになっていて難しくはありません。Helloコンポーネントは単一ファイルコンポーネントで実装されており、オブジェクトに変換されるので、公式ドキュメントに従って上記の単体テストの実装のようにコンポーネントをインポートして、動作を検証するコードを実装するだけです。

単体テストを実装したので、npm run unitコマンドで検証してみましょう。以下に抜粋するような検証がパスしているテスト結果が出力されることを確認できましたでしょうか?

  Helloコンポーネント
    ✓ デフォルトメッセージが正しく描画されていること
    ✓ msgプロパティで指定した文字列値で正しく描画されていること
    ✓ 親コンポーネント経由でmsgプロパティに指定された文字列で正しく描画されていること

PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 3 of 3 SUCCESS (0.02 secs / 0.012 secs)
TOTAL: 3 SUCCESS

今回は単体テストのみでしたが、Nightwatchを利用したE2Eテストも可能になっています。今回は解説しませんが、こちらも初期のテストコードがtest/e2e/specs配下にセットアップされているので、こちらにテストを実装してアプリケーションの動作検証をするとよいでしょう。

デバッグ

JavaScriptによるフロントエンドのアプリケーション開発においてデバッグにはいろいろ方法がありますが、そのうちの1つとしてコンソール出力(console.log)による、いわゆるプリントデバッグがあります。しかしながら、Vue.jsにおいてはコンポーネントの組合せによってツリー構造をもったUIとしてアプリケーションが構築されるため、この方法では非常に非効率です。Vue.jsは公式にvue-devtoolsというGoogle Chrome向けに拡張機能を提供しています。これを利用するとデバッグの効率が大きく上昇するでしょう。

本稿で作成しているアプリケーションをvue-devtoolsでデバッグしているときの画面の様子は図4のようになります。

図4 vue-devtools
図4 vue-devtools

上記のようにvue-devtoolsは、Web開発者がよく使用するDevtoolsに統合されておりVue.jsのコンポーネントをツリー形式でグラフィカルに確認することができます。また、コンポーネントの状態値(コンポーネントオプションのprops/data)の値も確認することができるため大変便利です。Vuexにも対応しているので、Vuexのstoreのデバッグも対応可能です。vue-devtoolsを利用してVue.jsアプリケーションのデバッグの生産性を高めることをお勧めします。

ビルド

単一ファイルコンポーネントを実装し、単体テストによるコンポーネントの動作検証、デバッグを学びました。いよいよリリースするためにビルドを行います。

一般にJavaScriptによるフロントエンドエンドのアプリケーションはビルドにwebpackやbrowserifyなどのツールを使用します。これらを使うことでJavaScriptはもちろん、HTML、CSSそして画像/フォントなどのアプリケーションの動作に必要なリソース一式をリリース可能なアセットとしてバンドリングできます。こういったリソースのバンドリングはいかにツールを使ったとしても、Babelなどのトランスパイラの設定、JavaScriptコードのミニファイ化/ソースマップ出力、警告メッセージをはじめとするデバッグコードの除去などの諸々の設定が必要になります。多くの場合は大変な作業です。

vue-cliで生成したVue.jsアプリケーションプロジェクトは、こういったビルドに必要なための設定作業がほぼゼロになるようになっています。具体的には、ビルド設定ファイルおよびビルドスクリプトをwebpack/browserifyのツールに応じてbuildディレクトリ配下に出力します。このため設定作業の必要はなく、すぐビルドできます。

それでは以下のコマンドでビルドしてみましょう。

$ npm run build

ビルド結果内容をビルドが完了し、結果が出力されます(出力メッセージ内容は実行環境によって異なります⁠⁠。

  Tip:
  Built files are meant to be served over an HTTP server.
  Opening index.html over file:// won't work.

⠋ building for production...cp: no such file or directory: static/*
Hash: 9c4ed13ce128ce3a2c03
Version: webpack 1.14.0
Time: 9823ms
                                                  Asset       Size  Chunks             Chunk Names
             static/js/manifest.c2ec5fc9f5c8f45dafe2.js  832 bytes       0  [emitted]  manifest
                  static/js/app.357c6f5c0fd1d34a3280.js    10.7 kB    1, 0  [emitted]  app
               static/js/vendor.db94a67cc8fd496820cf.js    74.2 kB    2, 0  [emitted]  vendor
    static/css/app.8d55793912407d0c8dcc48d76dc73b3b.css  259 bytes    1, 0  [emitted]  app
         static/js/manifest.c2ec5fc9f5c8f45dafe2.js.map    8.86 kB       0  [emitted]  manifest
              static/js/app.357c6f5c0fd1d34a3280.js.map    29.7 kB    1, 0  [emitted]  app
static/css/app.8d55793912407d0c8dcc48d76dc73b3b.css.map  660 bytes    1, 0  [emitted]  app
           static/js/vendor.db94a67cc8fd496820cf.js.map     604 kB    2, 0  [emitted]  vendor
                                             index.html  450 bytes          [emitted]

上記コマンドで、アプリケーションのリソース一式がビルドされたアセットはdistディレクトリに出力されます。こうしてビルドされたアセットを、Vue.jsアプリケーションとしてHTTPサーバにデプロイして配信可能になります。

さらに高度なアプリケーションを開発するためのその他の機能

Vue.jsにはこれまでの連載や今回紹介したvue-cli、単一コンポーネントの他にも、以下のような多くの高度な機能が利用できます。

  • スロット
  • トランジション
  • カスタイムディレクティブ
  • ミックスイン
  • プラグイン
  • 描画関数
  • サーバサイドレンダリング

この中から、スロットとトランジションをピックアップして簡単に紹介します。

スロット

スロットを利用してコンポーネントに対して外部からコンテンツを挿入できます。以下はスロットを使ったModalコンポーネントの例です。

<div class="modal-container">
  <!-- ヘッダー -->
  <div class="modal-header">
    <slot name="header">
      デフォルトヘッダー
    </slot>
  </div>
  <!-- ボディ --->
  <div class="modal-body">
    <slot name="body">
      デフォルトボディ
    </slot>
  </div>
  <!-- フッター -->
  <div class="modal-footer">
    <slot name="footer">
      デフォルトフッター
      <button class="modal-default-button" @click="$emit('close')"> OK </button>
    </slot>
  </div>
</div>

名前付きスロット(named slot)を利用して、モーダルのヘッダー、ボディ、フッターの各コンテンツ部分に外部から挿入できるようにしています。このModalコンポーネントを使用したコンテンツの挿入は以下です。

<modal v-if="showModal" @close="showModal = false">
  <!-- ここにモーダルのデフォルトコンテンツを上書きするカスタムコンテンツを使用できます。 -->
  <h3 slot="header">カスタムヘッダー</h3>
</modal>

上記の例では、h3要素にslot属性として"header"が指定されているので、Modalコンポーネントは定義された名前付きslotの"header"にこのコンテンツが挿入して描画します。このようにslotを利用することで再利用可能なコンポーネントを作成できます。

トランジション

要素が挿入、削除されるタイミングで遷移効果を与えるアニメーションを制御できます。以下は、transitionコンポーネントを使ったトランジションの例です。

<transition name="modal">
  <div class="modal-mask">
    <!-- ここには何らかのModalのテンプレートを定義 -->
    <!-- ... -->
  </div>
</transition>

Vue.jsのトランジションは、上記のようにトランジション対象となる要素をラップして使用します。CSSを使ったトランジションの定義も必要です。

.modal-enter {
  opacity: 0;
}

.modal-leave-active {
  opacity: 0;
}

.modal-enter .modal-container, .modal-leave-active .modal-container {
  -webkit-transform: scale(1.1);
  transform: scale(1.1);
}

上記では、要素が挿入されたときのCSSトランジション、要素が削除されたときのCSSトランジションを使用したトランジションクラスを定義しています。本稿で詳しく説明しませんが、このようなトランジションクラスは、Vue.jsのトランジションクラスの命名規則に従って定義する必要があります。Vue.jsのトランジションでは、要素の挿入・削除時にハンドリングして、これらCSSトランジションが定義されたトランジションクラスを対象となる要素のclass属性に適用することで、トランジションを実現しています。

以上、これまでのスロットとトランジションにおいて掲載した例をModalコンポーネントとして実装したものを、以下のjsfiddleに掲載しておきます。

スロットとトランジションの詳細や解説しなかったその他の機能については、日本語公式ドキュメントにて詳細を確認してください。

おわりに

最終回では、vue-cliを使ったアプリケーション開発について紹介しました。
vue-cliというVue.jsアプリケーションプロジェクトのテンプレートを生成するツールを利用することで、さまざまなツールの設定や開発環境のセットアップを簡略化して、すぐに開発に着手することを実感できたのではないでしょうか。

今回学習用に作成したアプリケーションプロジェクトは筆者のレポジトリに公開しています。参考にしてください。

本連載記事によって、Vue.jsのアプリケーションの作成ができるようにはなりました。Vue.jsの全てについて解説出来たわけではありません。さらにVue.jsは日々進化を続けています。ぜひ、日本語公式ドキュメントを読んで理解を深め、知識をアップデートしてください。

本連載の執筆メンバーで運営するSlackを使った日本人向けのVue.jsユーザーコミュニティもVue.jsを学ぶうえでは力になるはずです。こちらから登録できます。こちらのコミュニティでは、Vue.jsでわからないことの質問と回答、Vue.jsの事例や最新情報の共有が盛んに行われており、Vue.jsユーザー同士でコミュニケーションできる場となっております。興味がある方は、ぜひ登録してください。

それではご愛読ありがとうございました。

おすすめ記事

記事・ニュース一覧