今さら聞けない？Vue Router

## はじめに
本稿では、Vue Routerのセットアップ、使い方、その他Tipsについて解説します。

### Vue Routerとは
Vue.jsを利用したSPA構築で、ルーティング制御をするための公式プラグインです。

## 導入

### マニュアルセットアップ

**①npmでインストールする**

```
npm install vue-router
```

**②router.jsを作成する**

```js:src/router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home/'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    }
  ]
})
```
パス`/`にアクセスすると、`<router-view>`の中身がHomeコンポーネントとして置換される。
また、デフォルトの挙動ではURLに`#`が含まれるが、`mode:history`を指定することでURLからhashを取り除くことができる。

**③エントリポイントとなるJavaScriptファイルに記述を追加する**

```js:src/main.js
import router from './router' //追加

new Vue({
  router, //追加
  store,
  render: h => h(App)
}).$mount('#app')
```
プラグインとしての使用を宣言することで、`router.js`を他のコンポーネントにおいてインポートすることなく`this.$router`によってVue Routerにアクセス可能となる。

### Vue CLI 3によるセットアップ
Vue CLI 3を使用している場合、`vue create`をする際にvue-routerを選択するとプロジェクト初期化の際に自動的に組み込まれる。
また、`vue add vue-router`によって後から組み込みを行うことも可能。

## 使い方
ルーティング制御によって表示したいコンテンツの場所に、`<router-view>`タグを記述する。

```vue:src/App.vue
<template>
  <div id="app">
    <global-header/>
    <router-view/>
    <global-footer/>
  </div>
</template>

<script>
import GlobalHeader from './components/GlobalHeader'
import GlobalFooter from './components/GlobalFooter'

export default {
  components: {
    GlobalHeader,
    GlobalFooter
  }
}
</script>
```
例えば、上記のような記述を行うとヘッダー・フッターは常に共通のものが表示され、パスが変更されるとその間に配置された`<router-view/>`の内容がそのルートにマッチしたコンポーネントとして再レンダリングされる。

### ページ遷移
#### router-linkコンポーネントの利用

```vue
<router-link to="/">Go to home</router-link>
```
遷移先をto属性の値として指定する。
`<router-link>`タグは`<a>`タグとしてレンダリングされる。

#### プログラム的遷移

```js
this.$router.push('/')
```
Script内で上記の記述を行うことは、前項において記述した`<router-link>`タグをクリックすることと同値。

```js
this.$router.replace('/')
```
とすると、historyに追加せず遷移を行うことができる（ブラウザの戻す、進むボタンを利用して遷移した際の挙動が異なる）。

## Tips
### Dynamic Import
初期ロードの負荷を抑えるためにWebpackのCode Splittingを用いたDynamic Importを使ってみる。

```js:src/router.js
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

function loadView (view) {
  return () => import(/* webpackChunkName: "view-[request]" */ `@/views/${view}.vue`)
}

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: loadView('Home')
    },
    {
      path: '/about',
      name: 'about',
      component: loadView('About')
    }
  ]
})
```
上記のように変更を行うと、必要に応じてコンポーネントが遅延して読み込まれるようになる。

### 現在のパスを確認する
```js
this.$router.path
```
任意のコンポーネントにおいて上記記述を用いることで、現在のパスを取得できる。

### 画面遷移後のスクロール制御をする

```js:src/router.js
 routes: //...省略,
 scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0 }
    }
  }
```
例えばルーターオブジェクト内において上記記述を追加すると、ページ遷移ごとにスクロールが上端に戻る挙動となる。
`savedPosition`はオプションで、指定すると戻る/進むボタンによる遷移を行った際、そのページにいた状態のスクロールが再現される。

### 認証を確認する
認証済みであることを要求するページコンポーネントに対して、下記のように`ルートメタフィールド`を追加する。

```js:src/router.js
export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/sign-in',
      name: 'sign-in',
      component: loadView('PageSignIn')
    },
    {
      path: '/',
      name: 'home',
      component: loadView('Home'),
      meta: { requiredAuth: true } //Homeコンポーネントの表示には認証が必要と定義する
    }
  ]
})
```

ルートメタフィールドのrequiredAuthプロパティがtrueの場合、認証されているかどうかをチェックする挙動を実装する。
main.jsにナビゲーションガードを下記のように追加で記述する。

```js:src/main.js
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiredAuth)) {
    Auth.currentAuthenticatedUser() // 認証済みのユーザが存在するかどうかをチェックする関数
      .then(() => {
        next()
      })
      .catch(error => {
        console.error(error)
        next({
          path: 'sign-in',
          query: {redirect: to.fullPath}
        })
      })
  }
  next()
})
```

挙動は以下のようになる。

**①認証チェック**
ページ遷移先のルートにおいてrequireAuthがtrueとなっている場合、`Auth.currentAuthenticatedUser()`が実行される。

**②認証チェックの結果に基づいたページ遷移の実行**
認証済みユーザが存在する場合、正常にページ遷移が行われる。
認証済みユーザが存在しない場合、サインインページにリダイレクトされる。

本稿は以上となります。
誤り等ございましたら、ご指摘いただけますと幸いです。