Chapter9: Vue RouterでSPAを構築する　学習メモ

# Section 47 Vue Routerとは

「Vue Router」は、シングルページアプリケーションを構築する為のVue.jsの拡張ライブラリです。コンポーネントとURLを紐付けてコンテンツを生成します。

`用語` SPA: シングルページアプリケーション　Single-page Application 

#### Vue Routerのインストール

##### 最新版のインストール
```npm insall vue-router``` 

##### バージョン指定でインストール
```npm install vue-router@3.0.1``` 

##### Vue CLIでインストール

```vue add router``` 

####　組み込みコンポーネント

`<router-view>`
`<router-link>`

# Section 48 シンプルなSPA構造

#### 実際の操作でみましょう

``` vue create myrouter``` 
``` vue add router``` 
``` npm run serve``` 
``` Product page の追加操作```

#### URLにハッシュを付けない

src/router.js ファイルにて、` mode: 'history' ` を追加

##### apache の設定
```HTML:.htaccess
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase / 
  RewriteRule ^index¥.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f 
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>
```

##### ルーティング用プロパティ

例えば　this.$route.name 

などpage288にオブジエクト情報の記述あり

# Section 49 ルートの定義とオプション

#### 名前付きのルート

```javasctipt:src/router.js
const router = new VueRouter({
  routes: [
    {
     name: 'product', 
     path: '/product', 
     component: Product
    }
  ]
})
```

#### パラメータ
```javasctipt:src/router.js
const router = new VueRouter({
  routes: [
    {
     path: '/product/:id', 
     component: Product
    }
  ]
})
```

#### クエリー

`this.$route.query` 

#### メタフィールド

`meta: {requiresAuth: true}`

#### リダイレクト

`redirect: '/b'` 

# Section 50 ナビゲーションの作成

#### テンプレートによるナビゲーション

`<router-link to="/product">` 
`<router-link :to="'/product/${ id }'">` 
※ 自分の環境ではうまく行かなかったので下記のように
`<router-link :to="'/Product/' + id">`

```javascript:app.vue
<script>
export default {
    data () {
      return {
        id: 99
      }
    }
}
</script>
```

`<router-link to="/product" tag="button">` 

`<router-link :to="{ name: 'product', params: {id: 1}}">`

#### アクティブなリンクのハイライト

`.router-link-active {background: #e25193;}`
`.router-link-exact-active {background: #e25193;}`

>下記は.router-link-exact-activeと同じ効果

`<router-link to="/" exact>`

よく分からない：　`<router-link to="/product" replace>` 

#### プログラムによるナビゲーション

特定メソッド内で
`this.$router.push('/product/77')`

オブジェクトを渡す
`this.$router.push({name:'product', params:{id:77}})`

試し
`<button @click="move()"> move </button></p>`

``` 
export default {
  methods: {
    move: function(){
      this.$router.push('/product/777');
    }
  }
}
```

# Section 51 パラメータ付きの動的ルートからコンテンツを作成しよう

#### パラメータマッチのルーティング

```
const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Home
    },
    {
      path: '/product', // IDが付いていないときはリストを表示
      component: ProductList
    },
    {
      path: '/product/:id', // 「:id」がパラメータ 何が入ってもOK
      component: Product
    }
  ]
})
```

#### パラメータをpropsとしてコンポーネンットに渡す

router.jsにて

```
    {
      path: '/product/:id',
      name: 'product',
      component: Product, 
      props: true
    }
```

コンポーネントにて

```
<script>
export default{
    props: {id : Number}
}
</script>
```

`{{this.$route.param.id}}` を`{{ id }}` にすることができる

#### コンテンツを表示する

page299の内容を参考

操作は省略

# Section 52 ネストされた複雑なページを作成しよう

#### ネストされたルートの定義

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

import Home from '@/views/Home'
import ProductList from '@/views/ProductList' // 商品一覧
import Product from '@/views/Product' // 商品情報（親ルート）
// Productの子ルートたち
import ProductHome from '@/views/Product/Home'
import ProductReview from '@/views/Product/Review'
import ProductReviewDetail from '@/views/Product/ReviewDetail'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Home
    },
    // 商品一覧ページ
    {
      path: '/product',
      component: ProductList,
    },
    // 商品情報ページ
    {
      path: '/product/:id',
      component: Product,
      props: route => ({
        id: Number(route.params.id)
      }),
      children: [
        // 商品詳細（デフォルトルート）
        {
          name: 'product-home',
          path: '',
          component: ProductHome
        },
        // 商品のレビュー一覧
        {
          name: 'product-review',
          path: 'review',
          component: ProductReview
        },
        // 商品のレビュー詳細
        {
          name: 'review-detail',
          path: 'review/:rid', // 親ルートとかぶらないパラメータを指定
          component: ProductReviewDetail,
          props: route => ({
            rid: Number(route.params.rid)
          })
        }
      ]
    }
  ]
})
export default router
```

#### データの共有にはVuexを使用する

省略

#### 親ルート用コンポーネントの定義

省略

# Section 53 ナビゲーションガード

#### ナビゲーションガードの引数
`to` 遷移後のルートオブジェクト
`from` 遷移前のルートオブジェクト
`next` ナビゲーション解決用のコールバック関数

`next()`
`next(false)`
`next('/')` 
`next({path: '/' })`

#### ルート単位のガード page308

`beforeEnter` このルートの遷移前
 
```
beforeEnter(to, from, next){
  console.log('route:beforeEnter');
  next();
}
```

#### グローバルのガード page309

`beforeEach` 全てのルートの遷移前、コンポーネントガードの解決前
`beforeResolve` 全てのルートの遷移前、コンポーネントガードの解決後
`afterEach` 全てのルートの遷移後

#### このポーネントのガード page309
`beforeRouteEnter` このコンポーネントの遷移する前
`beforeRouteUpdate` このコンポーネントでルートが更新される前（パラメータの変更など）
`beforeRouteLeave` このコンポーネントから遷移する前

# Section 54 ページの遷移にエフェクトを適用する

#### 簡単なトランザクション
```
.view-enter-active, .view-leave-active{
  transition: opacity 0.5s;
}
.view-leave-active{
  position: absolute;
}
.view-enter, .view-leave-to {
  opacity: 0;
}
```

※あんまり理想的ではなかったのが、原因は？

#### 非同期読み込みを含むトランジション page312

- ルータービュー用のVuexモジュール
- グローバルのナビゲーションガード
- オーバーレイ用のコンポーネント
- コンポーネントのナビゲーションガード


# Section 55 その他の機能やオプション

#### 遷移前のデータを読み込むコンポーネント

```
<script>
import products from '@/api/products.js'
export default {
  data() {
    return {
      item: {}
    }
  },
  // enter は this を使用できないため実装が異なる
  beforeRouteEnter(to, from, next) {
    products.asyncFind(Number(to.params.id), item => {
      next(vm => {
        vm.item = item
      })
    })
  },
  beforeRouteUpdate(to, from, next) {
    products.asyncFind(Number(to.params.id), item => {
      this.item = item
      next()
    })
  }
}
</script>
```

#### コンポーネントの非同期読み込み

##### ルート用： 非同期読み込み

```
// 非同期コンポーネント
const About = () => import('@/views/About')
const router = new VueRouter({
  routes: [{
    path: '/about',
    component: About
    // 次のように書くこともできる
    // component: () => import('@/views/About')
  }]
})
```

##### オプション用: 非同期読み込み

```
<script>
export default {
  components: {
    MyComponent: () => import('@/components/MyComponent')
  }
}
</script>
```

#### ルートのアクセス制限

```
const router = new VueRouter({
  routes: [
    // アクセス制限したいルート
    {
    path: '/user',
    component: User,
    meta: {
      requiresAuth: true
    }
  }]
})

router.beforeEach((to, from, next) => {
  // 上位ルートを含めて認証が必要なルートがあるかを確認
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 認証状態を確認
    if (!auth.loggedIn()) {
      // 認証していなければログインページにリダイレクト
      next({
        path: '/login',
        query: {
          redirect: to.fullPath
        }
      })
    } else {
      next()
    }
  } else {
    next() // 認証の確認が必要ないルートならnext()で遷移
  }
})
```

#### スクロールの振る舞いを操作する

```
const router = new VueRouter({
  routes: [
    // ...
  ],
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return {
        x: 0,
        y: 0
      }
    }
  }
})
```

#### SPA の注意点

##### ハンドルの解除
```
export default {
  // ... 
  created() {
    window.addEentListener('scroll', this.handleScroll)
  }
  //beforeDestroyやdestroyedでハンドルを解除する！
  beforeDestroy(){
    window.removeEventListener('scroll', this.handleScroll)
  }
}
```

# まとめ
- Vue RouterはSPAを構築するためのルーティング用ライブラリ
- 必要なものだけを読み込むからパローマンスの最適化ができる
- URLに紐付いたページはコンポーネントで作成する
- ルートをネストすれば複雑なページ遷移もできる
- トランジションを使った動きのあるページ遷移を手軽に作れる