vue-routerチートシート


関連:
- [Vue.jsチートシート(基礎編)](https://qiita.com/morrr/items/289b6e7ac82e969a7a58)
- [Vue.jsチートシート(コンポーネントと構成編)](https://qiita.com/morrr/items/2642135a927d6a3e7584)

Vueチートシートに引き続き公式チュートリアルの抜粋です。


参考
------------------------------------------------------------
- [公式](https://router.vuejs.org/ja/)


概要
------------------------------------------------------------
- vue-router = Vue.jsにおける公式のルータ
    - SPAのためのページルーティング機能を提供する

- 公式によると以下を特徴としている:

    > - ネストされたルート/ビューマッピング
    > - モジュール式、コンポーネントベースのルータ構造
    > - ルートパラメータ、クエリ、ワイルドカード
    > - Vue.js の transition 機能による、transition エフェクトの表示
    > - 細かいナビゲーションコントロール
    > - 自動で付与される active CSS クラス
    > - HTML5 history モードまたは hash モードと IE9 の互換性
    > - カスタマイズ可能なスクロール動作

導入
------------------------------------------------------------
- CDNを [unpkg.com](https://unpkg.com/) が提供している

    ```html
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- Vue.js本体の後に記述すると自動ロードされる -->
    <script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"></script>
    ```

- webpack環境の場合はnpmを利用できる

    ```bash
    $ npm install vue-router
    ```

    ```js
    import Vue from 'vue'
    import VueRouter from 'vue-router'

    // use()によって明示的にロードする
    Vue.use(VueRouter)
    ```

基本
------------------------------------------------------------
- HTML側は:
    - `<router-view>` コンポーネントに現在の画面が表示される
    - `<router-link>` コンポーネントで画面遷移を明示する

    ```html
    <div id="app">
      <p>
        <router-link to="/foo">Foo</router-link>
        <router-link to="/bar">Bar</router-link>
      </p>

      <router-view></router-view>
    </div>
    ```

- js側は:
    - `VueRouter` を作成しrootインスタンスに渡すことでルーティングを設定する

        ```js
        // ルーティングを定義する
        const router = new VueRouter({
          routes: [
            { path: '/foo', component: Foo },
            { path: '/bar', component: Bar }
          ],
        })

        // rootインスタンスに渡す
        const vm = new Vue({
          router
        }).$mount('#app')
        ```

    - `this.$route` でそのルーティングの情報、 `this.$router` でルーティング全体の情報にコンポーネントからアクセスできる

        ```js
        export default {
          computed: {
            username () {
              return this.$route.params.username
            }
          },
          methods: {
            goBack () {
              window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
            }
          }
        }
        ```


ルーティング方法
------------------------------------------------------------
### 動的なマッチング

- Rails等と同じく、コロン : を使ってパラメータを含むルーティングを設定できる

    ```js
    const router = new VueRouter({
      routes: [
        { path: '/user/:id', component: User }
      ]
    })
    ```

- URLが含むパラメータは `$route.params` で参照できる

    ```js
    const User = {
      template: '<span>{{ $route.params.id }}</span>'
    }
    ```

- 同じrouteの異なるパラメータで画面遷移しても、vue-routerは画面を再描画しない
    - ライフサイクルフックが呼ばれないため、変更を検知したい場合はwatchを使う

    ```js
    {
      watch: {
        '$route' : (to, from)=> ...
      }
    }
    ```

- マッチパターンのワイルドカードとして `*` が使える
    - マッチしたパターンは `$route.params.pathMatch` で参照できる
    - ルーティングは先に宣言されたパターンのほうが優先される

    ```js
    const router = new VueRouter({
      routes: [
        { path: '/user-*', component: User },
        { path: '*', component: NotFound },             // 全てにマッチ
      ]
    })
    ```

### ネスト
- ネストされたルーティングに対し、 `<view-router>` もネストすることで対応できる

    ```html
    <div id="root">
      <!-- トップレベル -->
      <router-view></router-view>
    </div>
    ```

    ```js
    const User = {
      template: `
        <div class="user">
          <!-- 2階層目のview -->
          <router-view></router-view>
        </div>
      `
    }
    
    const router = new VueRouter({
      routes: [
        {
          path: '/user/:id',
          component: User,
          children: [
            // /user/:id/profileにマッチした時に2階層目のrouter-viewに表示される
            { path: 'profile', component: UserProfile }

            // /user/:id の場合に2階層目に表示させたい場合
            { path: '', component: UserDefault }
          ]
        }
      ]
    })
    ```

### 同階層に複数のviewを配置する
- 複数のviewを並列したい場合は名前をつけて管理する

    ```html
    <!-- 未指定は "default" となる -->
    <router-view ></router-view>
    <router-view name="a"></router-view>
    <router-view name="b"></router-view>
    ```

    ```js
    // ルーティングの際にcomponentではなくcomponentsを指定する
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          components: {
            default: Foo,
            a: Bar,
            b: Baz
          }
        }
      ]
    })
    ```

### コードによる制御
vue-routerのページ履歴はES標準の `window.history` に近似する


- `<router-link>` によって静的なリンクを作成するほか、コードにより画面遷移をトリガできる

    ```js
    // 単純な遷移
    this.$router.push('/user/123')

    // 名前による指定
    // /user/:id のrouteがあった場合に /user/123 に遷移する
    this.$router.push({ name: 'user', { id: '123' }})

    // パラメータを渡す
    // /foo?bar=baz
    this.$router.push({ path: 'foo', query: { bar: 'baz' }})
    ```

- `replace()` を使うとhistoryにURLをスタックせずに遷移できる

    ```js
    this.$router.replace('/user/123')
    ```

    ```html
    <!-- router-linkも同様にreplace指定できる -->
    <router-link to="/foo" replace></router-link>
    ```

- `go()` を使うと指定した数のスタックを進んだり戻ったりできる

    ```js
    // 進む
    this.$router.go(1)
    
    // 戻る
    this.$router.go(-1)
    ```

### 名前付きのroute
- routeには名前をつけることができる

    ```js
    const router = new VueRouter({
      routes: [
        {
          path: '/user/:userId',
          name: 'user',
          component: User
        }
      ]
    })
    ```

- つけた名前は `<route-link>` あるいは `$router.push()` で指定できる

    ```html
    <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
    ```

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

### リダイレクト
- リダイレクトを設定できる

    ```js
    const router = new VueRouter({
      routes: [
        { path: '/a', redirect: '/b' }
      ]
    })

    // 動的にリダイレクトさせることもできる
    const router = new VueRouter({
      routes: [
        { path: '/a', redirect: to => {
          return '/b'
        }}
      ]
    })
    ```

### エイリアス
- エイリアスはリダイレクトと異なり、最初にアクセスしたURLを書き換えない

    ```js
    // /b にアクセスするとURLはそのまま、/a の内容が表示される
    const router = new VueRouter({
      routes: [
        { path: '/a', component: A, alias: '/b' }
      ]
    })
    ```

### $route をpropsで渡す
- `this.$route` をコンポーネント内で直接付参照すると、特定のrouteとコンポーネントが密結合してしまう
    - propsを介してroute固有の情報を受け渡すことで、結合を疎にできる

    ```js
    const User = {
      // $this.route.params.idを直接参照しなくていいので、その他の方法でIDを渡すこともでできる
      props: ['id'],
      template: '<div>User {{ id }}</div>'
    }

    const router = new VueRouter({
      routes: [
        // props設定を有効にすることで、$route.paramsの内容がそのままpropsとして渡る
        { path: '/user/:id', component: User, props: true },

        // Functionで指定することもできる
        { path: '/user/:id', component: User, props: (route) => (...) },
      ]
    })
    ```

### HTML5 History
- routerはhashモードとhistoryモードがある
    - hash: デフォルトのモード。内部的なハッシュを使って画面遷移をコントロールする
    - history: HTML5のhistoryオブジェクトの機能を利用するモード。hashと同じくページはリロードされないが、URLが書き換わる

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

- historyモードはユーザが直接URLを叩くと、サーバが対応できずに404になる可能性がある
    - 404の場合はルートリダイレクトする、などサーバ側の対処が必要になる