Vue.jsを利用したシングルページアプリケーション(SPA)作成超入門

# シングルページアプリケーションとは
シングルページアプリケーションは最初のブラウザでのアクセスでページ全体を取得し、その後、ページ内のリンク等をクリックしてもページ遷移を行わず、必要な箇所のみ描画し直す方式のWebアプリを言います。
GmailやSlackなどがそれに該当します。
今回は私がVue.jsを利用してシングルページアプリケーションを実装しようと奮闘した順にしたがって実装方法を説明しようと思います。

# レベル0 力技
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="GRxypYO" data-user="takayuki332" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/takayuki332/pen/GRxypYO">
  Qiita SPA LEVEL1</a> by TakayukiTakase (<a href="https://codepen.io/takayuki332">@takayuki332</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

上記サンプルでは`v-if`を利用してコンポーネントを出し分けました。
vue.jsをあまり勉強をせずにシングルページアプリケーションを実装しようとした時にきっと多くの人(私を含む)が通った道だと思いますが、この方法だとページが増えるごとに`v-if`が増え、可読性が下がりそうです。
しかし、少し勉強したらこんな便利タグが存在することに気が付きます。それをレベル1で紹介します。

# レベル1 componentタグを使って実装
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="XWEVgXO" data-user="takayuki332" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/takayuki332/pen/XWEVgXO">
  Qiita SPA LEVEL2</a> by TakayukiTakase (<a href="https://codepen.io/takayuki332">@takayuki332</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

上記のサンプルでは`component`タグを使ってコンポーネントの切り替えを行っています。

## componentタグとは
```html
<component is="hoge-component" />
```
上記のように`is`にコンポーネント名を設定することでコンポーネントタグを埋め込んだところに指定したコンポーネントを表示することができます。
これを以下のようにv-bindを利用することでリアクティブにコンポーネント切り替えることができるようになります。
```html
<component v-bind:is="page" />
```
レベル0と比べてかなりいい感じです。レベル0ではページが増えれば増えるほど`v-if`の分岐が増え可読性が悪くなりそうでしたが、その問題が解決しました。
しかし、ここで問題が発生します。Webページってページが遷移するとURLが書き変わりますが、この方法でシングルページを作ってもURLとページを紐づけることが煩雑です。URLに紐づけるのはもちろん、ブラウザバックでヒストリーを残すことが大変になります。
しかし、少し勉強したらこんな便利な拡張機能が存在することに気が付きます。それをレベル2で紹介します。

# レベル2 Vue Routerを使って実装
※ここではVue Routerの導入方法は割愛します。各自で調べてください。

## vue createコマンドで作成したプロジェクトを書き換える
以下のファイルをサンプルの内容に書き換え・新規作成を行います。
```html:src/App.vue
<template>
  <router-link to="/" style="padding-right: 10px;">ページA</router-link>
  <router-link to="/pageb">ページB</router-link>
  <router-view/>
</template>
```
```html:src/views/PageA.vue
<template>
  <div><h1>ページAです</h1></div>
</template>
```
```html:src/views/PageB.vue
<template>
  <div><h1>ページBです</h1></div>
</template>
```
```js:src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import PageA from '../views/PageA.vue'
import PageB from '../views/PageB.vue'

const routes = [
  {
    path: '/',
    component: PageA
  },
  {
    path: '/pageb',
    component: PageB
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router
```
この状態で以下のコマンドを実行し、`http://localhost:8080/`にアクセスすると各レベルで作成したSPAを利用できます。
```bash
npm run serve
```
実際にURLの変化と同時にそれに対応したコンポーネントが表示されていることが確認できると思います。

## 説明
### router-linkタグって?
```html
<router-link to="/hoge">hoge</router-link>
```
`to`にrouterに登録されているパスを指定します。`src/router/index.js`の中身を見てもらえると直感的にわかると思います。
router-linkタグはブラウザ上ではaタグとして表示されます。
### router-viewタグって?
```html
<router-view/>
```
routerによってURLに対応したコンポーネントがrouter-viewタグを設置した場所に表示されます。
### indexjsのrouterって?
routerによってパスとどのコンポーネントが対応しているかを紐づけています。サンプルのコードでも
`const routes`の記述を見ていただけると直感的にわかりやすいと思います。

# 最後に
いろんなことを端折る駆け抜けましたが、Vueのルーティングの機能はまだまだたくさんあります。
Vue.jsにおけるシングルページアプリケーションの最適解は今の所きっとルーティングの拡張機能だと思うので私自身、これからルーティングの学習を進めていきたいと思います。