Vue.jsでSPAサイトを作成するチュートリアル【2. ルーター編】

Vue.jsを使い始めていろいろできることが多くなってきたので、整理する意味も兼ねてチュートリアルにまとめます。
今回はコーポレートサイトを想定して作成していきます。
※記事が長くなったのでチュートリアルを分割しました。


## 目次

* [表示編](https://qiita.com/buchiya4th/items/cff0b2ab9ebd550c4e0f)
* ルーター編 ←イマココ
* [コンポーネント編](https://qiita.com/buchiya4th/items/d5a62722f1fc26230df0)
* [CSS編](https://qiita.com/buchiya4th/items/6f5db9f58ee66982438e)
* [JS編](https://qiita.com/buchiya4th/items/3f3b9c6e48866e6d9d27)
* [ライブラリ編](https://qiita.com/buchiya4th/items/b4814c0018f9311af461)
* [メタ編](https://qiita.com/buchiya4th/items/dd8f16dfcd296bd247bb)
* [ビルド編](https://qiita.com/buchiya4th/items/71649f8fbd2f72b9e6e7)


## 前提

* タスクは`npm scripts`で一限管理
* コマンドは`yarn`を使用
* `vue-cli`の`webpack-simple`を使用


## バージョン

* "vue": "^2.5.11"
* "vue-router": "^3.0.1"
* "webpack": "^3.6.0"


## SPAサイトの全体像

今回作成するSPAサイトの全体像は以下です。

* index
* about
* service
* recruite
* contact

ディレクトリ構成は以下です。
/pagesに各ページのディレクトリを作成し、templateとstyleとscriptを各外部ファイルとして扱います。

```txt
/src
  /layouts
    Default.vue
  /pages
    /Index
      Index.vue
      Index.pug
      Index.scss
      Index.js
    /About
      About.vue
      About.pug
      About.scss
      About.js
    /Service
      Service.vue
      Service.pug
      Service.scss
      Service.js
    /Recruite
      Recruite.vue
      Recruite.pug
      Recruite.scss
      Recruite.js
    /Contact
      Contact.vue
      Contact.pug
      Contact.scss
      Contact.js
  App.vue
  main.js
  router.js
  index.html
  // 以下省略
```

ルーターを実装する前に動作確認用としてindexとaboutを先に作成しておきましょう。

HTMLは今回はpugを使用します。
pugを追加します。

```bash
$ yarn add -D pug
```

indexページを作成します。

```html:/src/pages/Index/Index.vue
<template
  lang="pug"
  src="./Index.pug"
/>

<script src="./Index.js" />

<style
  lang="scss"
  src="./Index.scss"
  scoped
/>


```

```pug:/src/pages/Index/Index.pug
main.main
  p {{ text }}
  .block
    | block

```

```js:/src/pages/Index/Index.js
export default {
  data: function () {
    return {
      text: 'index'
    }
  }
}

```

```scss:/src/pages/Index/Index.scss
.block {
  color: #f00;
  display: flex;
}

```

同様にaboutページもvue, pug, js, scssの各ファイルを作成しましょう。
vueファイルのtemplateはpugで統一したいので、App.vueのtemplateもpugで書き直しておきましょう。


```html:App.vue
<template lang="pug">
#app.app
  img(src="./img/logo.png")
</template>

```


## vue-routerの環境構築

vue-routerを追加します。

```bash
$ yarn add vue-router
```

ルーターファイルを追加し記述します。
/layouts/Default.vueのコンポーネントの子として各ページをchildren内に記述します。

```js:/src/router.js
export default [
  {
    path: '/',
    name: 'index',
    component: require('./layouts/Default.vue').default,
    children: [
      {
        path: '',
        name: 'index',
        component: require('./pages/Index/Index.vue').default,
      },
      {
        path: '/about/',
        name: 'about'
        component: require('./pages/About/About.vue').default
      }
      // 省略
    ]
  }
]

```

参考：[ネストされたルート](https://router.vuejs.org/ja/essentials/nested-routes.html)

main.jsを以下に差し替えます。

```js:main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import Routes from './router.js'

Vue.use(VueRouter)

const router = new VueRouter({
  mode: 'history',
  routes: Routes
})

const app = new Vue({
  router,
  render:h => h(App)
}).$mount('#app');

```

App.vueを以下に書き換えます。

```html:/src/App.vue
<template lang="pug">
#app.app
  router-view
</template>
```

共通レイアウトを作成します。

```bash
$ touch src/layouts/Default.vue
```

Default.vueに記述します。

```html:/src/layouts/Default.vue
<template lang="pug">
.wrap
  p header
  router-link(to="/") index
  router-link(to="/about/") about
  router-link(to="/service/") service
  router-link(to="/recruite/") recruite
  router-link(to="/contact/") contact
  main#main.main router-view
  p footer
</template>

```

ローカルで作業する際にbrouser-syncの以下の設定をしておくと、各URLに直接アクセスできるようになります。

参考：[react-routerとbrowser-syncを利用して快適に開発するちょっとした設定](https://qiita.com/hikaruworld@github/items/33c5302c15c045c4df4a)

ブラウザで確認してページ遷移してindexページとaboutページがそれぞれ表示されれば成功です。
その他各ページも同様に作成していきましょう。
その際に、router.jsに各ページの指定も忘れないように追記しましょう。
遷移するか確認するためにDefault.vueのrouter-linkを追記すると良いでしょう。


## ページ遷移時の表示位置を最適化

ページ遷移した時に中途半端な位置で表示することがあります。
他のページへ遷移した場合はページトップ、ブラウザの「戻る」「進む」ボタンを押すと遷移前の位置を表示するよう最適化します。

```js:main.js
// 省略
const router = new VueRouter({
  mode: 'history',
  routes: Routes,
  scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0 }
    }
  }
})
// 省略
```

参考：[スクロールの振る舞い](https://router.vuejs.org/ja/advanced/scroll-behavior.html)


## まとめ

ルーターはSPAでは重要な役割を果たしていて、ページ間のやりとりに重宝します。
最近のウェブサイトはページの非同期遷移をおこなっているものが増えてきました。
積極的に取り入れてユーザー体験を向上させましょう。