Vue.jsとVuexとvue-routerとwebpackで単一ファイルコンポーネント化してTodoListを作ってみた

# はじめに

前回の「[Vue.jsとVuexとwebpackで単一ファイルコンポーネント化してTodoListを作ってみた](http://qiita.com/tiwu_official/items/6a877bcdf2c3022f0ff6)」にvue-routerをいれて、Single Page Applicationにしてみたので書いていきます。

※タイトルがどんどん長くなる・・・

[成果物](https://theimaginationwasunbroken.com/vue-sample/src/vuex-webpack-components-vue-router-todo-sample/src/)

機能は前回と同じく「追加」と、「完了」と、「完了から戻す」3つだけ。

Vue.jsとVuexとwebpackと単一ファイルコンポーネントの簡単な説明は前回のQiitaに書いたので割愛。

# スペック

- vue-routerは少しだけ使ったことある
- SPAっぽいものを実装した経験がある

# vue-router

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

Vue.jsでSPAを作る際のライブラリ。

## router-link

vue-routerでリンクを作成するコンポーネント。

aタグのようなもの

```html
<router-link to="/foo">Go to Foo</router-link>
```

## router-view

SPAで書き換わる中身のコンポーネント。

下記例ではfooやbarに遷移すると`router-view`は書き換わるがh1とかは書き替わらない。

```html
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <router-view></router-view>
</div>
```

# ソースコード解説

[全体](https://github.com/tiwuofficial/vue-sample/tree/master/src/vuex-webpack-components-vue-router-todo-sample)

## package.json

```json

{
  "name": "vuex-webpack-components-vue-router-todo-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack",
    "dev-watch": "webpack --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.4.2",
    "vue-router": "^2.7.0",
    "vuex": "^2.4.0"
  },
  "devDependencies": {
    "css-loader": "^0.28.7",
    "vue-loader": "^13.0.4",
    "vue-template-compiler": "^2.4.2",
    "webpack": "^3.5.6"
  }
}
```

前回から追加したものは`vue-router`と`scripts`に`dev-watch`を追加しました。

今まではビルドの度に毎回webpackを動かしていましたが、watchオプションをつけ自動でビルドさせています。

## webpack.config.js

前回と差分なし

## index.html

```html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Vuex Vue-Router Components Todo Sample</title>
  <link rel="stylesheet" href="">
  <style>
    main {
      display: flex;
    }
  </style>
</head>
<body>
  <div id="app">
    <header>
      <router-link to="todoInput">Go to TodoInput</router-link>
      <router-link to="todoList">Go to todoList</router-link>
      <router-link to="doneList">Go to doneList</router-link>
    </header>
    <router-view></router-view>
  </div>
  <script src="js/bundle.js"></script>
</body>
</html>
```

`router-link`コンポーネントを利用し、3つのリンクを作成。

また、`router-view`コンポーネントを記述しています。

## app.js

```js
import Vue from 'vue';
import Vuex from 'vuex';
import VueRouter from 'vue-router';
import routes from './router/routes.js';
import actions from './action/actions.js';
import mutations from './mutation/mutations.js';
Vue.use(Vuex);
Vue.use(VueRouter);

var store = new Vuex.Store({
  state: {
    todos: [],
    dones: []
  },
  getters: {
    todos(state) {
      return state.todos;
    },
    dones(state) {
      return state.dones;
    }
  },
  actions: actions,
  mutations: mutations,
});

const router = new VueRouter({
  routes
});

new Vue({
  el: '#app',
  store: store,
  router: router,
});
```

すっきりとしたJSに！

actionsとmutationsはそのまま別JSファイルに出しました。

※stateとgettersも出したほうが良い気がしたが今回はそのまま

VueRouterの引数にroutesを渡して生成し、Vueに渡してvue-routerの設定は終わり。

## routes.js

```js
import indexView from './../view/indexView.vue';
import todoInputView from './../view/todoInputView.vue';
import todoListView from './../view/todoListView.vue';
import doneListView from './../view/doneListView.vue';

export default [
  { path: '/', component: indexView },
  { path: '/todoInput', component: todoInputView },
  { path: '/todoList', component: todoListView },
  { path: '/doneList', component: doneListView },
];
```

今回のメインのvue-routerのルーティング設定ファイル。

vue-routerの[公式](https://router.vuejs.org/ja/essentials/getting-started.html#)は一つのJSに書かれていますが、今回は別ファイルにしました。

各URLとコンポーネントを紐付けています。

`/todoInput`にアクセスすると、`router-view`に`todoInputView.vue`が表示されます。

## todoInputView.vue

```html
<template>
  <section class="wrap">
    <h2>Todo Input</h2>
    <todo-input></todo-input>
  </section>
</template>

<style scoped>
  .wrap {
    padding: 0 10px;
  }
</style>

<script>
  import todoInput from './../components/todoInput.vue';
  export default {
    components: {
      todoInput
    },
  }
</script>
```

単一ファイルコンポーネント。

`todoInput.vue`をimportし`<template>`内で表示しています。

## todoInput.vue

```html
<template>
  <input type="text" @keyup.enter="addTodoText"/>
</template>

<script>
  export default {
    methods: {
      addTodoText(e) {
        var text = e.target.value;
        this.$store.dispatch('addTodo', {
          text: text
        });
        e.target.value = '';
      }
    }
  }
</script>
```

`input`要素と挙動だけ書かれた単一ファイルコンポーネント。

# 所感

vue-roterの導入自体は難しくなくすんなりできた。