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

第4回シングルページアプリケーションの基礎を作成する

はじめに

この連載の前回までの記事で解説にあったように、Vue.jsはシンプルなViewレイヤのライブラリです。もしアプリケーションにシングルページアプリケーションとしての要件がある場合、ライブラリとしてVue.jsのみを使用した実装では少し困難になるでしょう。公式プラグインであるVue Routerを使えば、SPAを簡単に実現できるようになります。連載第4回目である本稿では、Vue.jsとVue Routerを使ったSPAの実装の基本について紹介します。

シングルページアプリケーションとは

シングルページアプリケーション(以下SPA)とは、1つのHTMLをロードして、ユーザーインタラクションに応じて動的にページを更新するWebアプリケーションです。通常のWebアプリケーションでは、ページ遷移時にサーバへアクセスしコンテンツをロードしますが、SPAではページ遷移をクライアントサイドで行います。その際に、Ajaxを使用して必要な時に必要なデータを取得してViewの表示を行います。そのため、より滑らかなユーザー体験を提供できるようになります。

一般的にSPAを実装するには、クライアントサイドでのページ遷移、データ取得、Viewのレンダリング、モジュール化されたコードの管理など、多くのことを考慮する必要があります。それらの機能を担ってくれるのがルーターと呼ばれるモジュールです。Reactに代表されるような最近の人気JavaScriptライブラリの多くは、ルーターの機能を本体もしくはプラグインとして提供しています。

Vue Routerの基礎

ルーターのインストール

Vue.jsのプラグインとして公式に提供されているSPA構築のためのルーティングライブラリがVue Routerです。Vue Routerは、ページ遷移先URLに対応するViewのレンダリング管理機能の他にも、便利なフック関数や遅延ローディングなどの機能を持っています。インストールするにはスクリプトタグでVue.js本体に続けて読み込んでください。以下のようにjsdelivrなどのCDNサービスを使うと便利です(執筆時点でのVue Routerの最新バージョンはv2.0.1です⁠⁠。

<script src="https://cdn.jsdelivr.net/vue/2.0.3/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/vue.router/2.0.1/vue-router.min.js"></script>

ルートの定義

ルートとは、URLとViewの情報を保持する1つのレコードです。Vue Routerにおけるルートは、前回の記事で解説したVue.jsのコンポーネントを特定のURLにマッピングしたオブジェクトとして、ルーター初期化時のroutesオプションに設定されます。

// ルートオプションを渡してルーターインスタンスを生成します
var router = new VueRouter({
  // 各ルートにコンポーネントをマッピングします
  // コンポーネントはVue.extend() によって作られたコンポーネントコンストラクタでも
  // コンポーネントオプションのオブジェクトでも構いません
  routes: [
    {
      path: '/top',
      component: {
        template: '<div>トップページです。</div>'
      }
    },
    {
      path: '/users',
      component: {
        template: '<div>ユーザー一覧ページです。</div>'
      }
    }
  ]
})

// ルーターのインスタンスをrootとなるVueインスタンスに渡します
var app = new Vue({
  router: router
}).$mount('#app')

これだけで基本的なルートの定義は完了です。

SPAを実装していると、ユーザがアクセスするURLに対してパターンマッチングさせたいケースもあるでしょう。そのような場合はpath内のURLに:を使用してパターンを記述しましょう。マッチしたURL上のパラメータはコンポーネント内の$route.paramsからパターンに使用したパラメータ名と同じ名前でアクセスして取得することができます。

たとえば、ユーザの詳細ページを/user/:userIdというURLで受け付けて、URL内に含まれるユーザのIDに応じて表示を切り替えるようなUIを考えてみましょう。以下のコード例では、/user/123へアクセスがあった時に、コンポーネント内部でアクセスできる$route.params.userIdの値は123になります。

var router = new VueRouter({
  routes: [
    {
      // コロンで始まるパターンマッチング
      path: '/user/:userId',
      component: {
        template: '<div>ユーザーIDは {{ $route.params.userId }} です。</div>'
      }
    }
  ]
})

ページ遷移の実行

URLがマッチした時にマッピングされたコンポーネントをレンダリングして出力する先をrootのVueインスタンスがマウントしているHTML内に指定しましょう。<router-view>タグを使用します。

<div id="app">
  <!-- 現在のURLとマッチしたルートに定義されたコンポーネントがここにレンダリングされます -->
  <router-view></router-view>
</div>

この例ではブラウザで直接入力してアクセスしたURLとルート内でマッピングしたコンポーネントが<router-view>の部分にレンダリングされます。ただし、このままではページを開いてからページ遷移ができません。リンクを表示してページ遷移をできるようにしましょう。リンクの定義には<router-link>タグを使用します。

<div id="app">
  <!-- リンク先を `to` プロパティに指定します -->
  <!-- デフォルトで <router-link> は `<a>` タグとしてレンダリングされます -->
  <router-link to="/top">トップページ</router-link>
  <router-link to="/users">ユーザー一覧ページ</router-link>
  <router-view></router-view>
</div>

jsfiddleのサンプルを実行してみてください(jsfiddleではiframeを用いているためブラウザー上に表示されているURLは変更されません⁠⁠。

以上、わずかなコード量でシンプルなSPAの基礎ができました。

名前付きルート

SPAでページ遷移をさせたい時に、ルートの名前を付けて指定できると便利なことがあります。Vue Routerではルートを定義した時に名前を付与して、その名前を指定してページ遷移を実行できます。

以下、/user/:userIdというpathに名前をつけてルートを定義する例です。

var router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'user',
      component: {
        template: '<div>ユーザーIDは {{ $route.params.userId }} です。</div>'
      }
    }
  ]
})

上記の名前付きルートを呼び出すには、<router-link>toパラメータに指定します。同時にURLパターンへのパラメータも同時に渡すことができます。

<router-link :to="{ name: 'user', params: { userId: 123 }}">ユーザー詳細ページ</router-link>

router.pushを使った遷移

上で紹介したhtml内の宣言的な書き方である<router-link>の代わりに、router.pushを使ったプログラミングによる遷移も可能です。

router.push({ name: 'user', params: { userId: 123 }})

渡される引数は<router-link>to プロパティで受け取るオブジェクト同じものなので、上記のように名前付きルートを使った遷移も可能です。

ネストされたルート

アプリケーションが少し複雑になってくると、ネストされたURLを使って、それに対応したコンポーネントを定義したいケースがあります。たとえば、ユーザ詳細ページの中で、プロフィール情報を表示するコンポーネントと、そのユーザが投稿した情報を表示するコンポーネントの表示を、URLに応じて部分的に切り替えたい時などです。そのようなケースも、Vue Routerを用いると簡単に実現できます。

// ユーザー詳細ページのコンポーネント定義
var User = {
  template:
    '<div class="user">' +
      '<h2>ユーザーIDは {{ $route.params.userId }} です。</h2>' +
      '<router-link :to="\'/user/\' + $route.params.userId + \'/profile\'">ユーザーのプロフィールページを見る</router-link>' +
      '<router-link :to="\'/user/\' + $route.params.userId + \'/posts\'">ユーザーの投稿ページを見る</router-link>' +
      '<router-view></router-view>' +
    '</div>'
}

// ユーザー詳細ページ内で部分的に表示されるユーザーのプロフィールページ
var UserProfile = {
  template:
    '<div class="user-profile">' +
      '<h3>こちらはユーザー {{ $route.params.userId }} のプロフィールページです。</h3>' +
    '</div>'
}

// ユーザー詳細ページ内で部分的に表示されるユーザーの投稿ページ
var UserPosts = {
  template:
    '<div class="user-posts">' +
      '<h3>こちらはユーザー {{ $route.params.userId }} の投稿ページです。</h3>' +
    '</div>'
}

var router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'user',
      component: User,
      children: [
        {
          // /user/:userId/profile がマッチした時に
          // UserProfileコンポーネントはUserコンポーネントの <router-view> 内部でレンダリングされます
          path: 'profile',
          component: UserProfile
        },
        {
          // /user/:userId/posts がマッチした時に
          // UserPostsコンポーネントはUserコンポーネントの <router-view> 内部でレンダリングされます
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
})

こちらのjsfiddleが動作する例です。/user/123としてアクセスしたユーザ詳細ページからそれぞれプロフィールページと投稿ページをクリックした際にページ内の該当箇所が部分的に更新されているのがわかります。

なお、今回の連載ではメジャーなブラウザが直接解釈可能な、ECMAScript 5準拠のコードを用いて解説しています。現時点でサポートされるブラウザは少し限られますが、ES2015のTemplate literalを使用してコンポーネントのtemplateを記述すると、より明瞭な記述が可能になります。興味のある方は、Template literalを使ったコード例をご参照ください。

リダイレクトとエイリアス

状況に応じて、SPAでも通常のWebアプリケーションと同様に、リダイレクトの機能を使用したいケースが出てくることもあるでしょう。Vue Routerは、実行した時にURLを書き換えるリダイレクトと、URLは書き換えずルーティング処理を実行するエイリアスを使用することができます。

リダイレクト

以下のリダイレクトのコード例では、/aへアクセスした時に/bへ遷移します。その時URLも遷移先のものに書き換わります。また、*を使うことで定義している全てのルートにマッチしなかった時のリダイレクト先を指定することもできます。1つの代表的な例として、Not Foundページを作る時に便利です。

var router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' },
    { path: '/b', component: B },
    { path: '/notfound', component: NotFound },
    // 現在のURLが定義したルートのいずれにもマッチしなかった時に/notfoundに遷移する
    { path: '*', redirect: '/notfound' }
  ]
})

エイリアス

URL上はアクセスした時のものを保持した状態で、別のルートで定義したものとして遷移の処理を実行させたい時に、aliasが使えます。以下の1つめの例では、/bへアクセスした時にURL上は/bのままですが、コンポーネントAがレンダリングされ、あたかも/aへアクセスしたかのように振る舞います。2つめの例のようにaliasは複数指定することもできます。

var router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
    { path: '/c', component: C, alias: ['/d', '/e'] }
  ]
})

履歴の管理

SPAではサーバ側のルーティングを介していないため、ブラウザの戻る・進むボタンを押した時の履歴操作もクライアント側で管理しなくてはなりません。履歴管理を実現するための方法にはURL Hashを使った方法とHTML5 History APIを使った方法があります。

URL Hash

URL Hashを使った場合、URLの末尾に#/が付与され、ルーティングのパスを管理します。クライアント側でURLが変更されるため、ブラウザの履歴に追加されます。ブラウザの戻る・進むボタンを押した際には、内部的にhashchangeイベントを使ってルーティングの変更時の処理が行われます。

HTML5 History API

もう1つの履歴の管理方法として、HTML5から導入された履歴スタックを操作できるHTML5 History APIがあります。こちらを使った場合、#/は付与されず、URLが通常のサーバサイドで遷移を行った時と同じ形式になります。このモードではユーザが直接ブラウザで該当のURLを入力してアクセスした時に、サーバ側がエラーを起こさずに適切にSPAのページを返す処理をしなくてはならない点に注意が必要です。

Vue RouterはデフォルトでURL Hashとして動作します。'hash'もしくは'history'をVue Routerインスタンス生成時にmodeオプションとして指定することで切り替えることができます。

var router = new VueRouter({
  mode: 'history',
  routes: [...]
})

まとめ

いかがでしたか。一見難しそうに見えるSPAの実装がVue.jsとVue Routerを組み合わせることで簡単に実現できるようになります。次回はVue Routerが提供するデータの取得やフック関数についてなど、SPAを構成するためのもう少し高度な機能と実装例について紹介します。お楽しみに。

おすすめ記事

記事・ニュース一覧