そんなときに便利なのが「vuex-router-sync」というライブラリ。
これを使うとstore.state.routeでroute情報にアクセスすることができる。
ということで、vuex-router-syncを使ったサンプルコードを書いてみた。
記事の最後に、vuex-router-syncの詳細仕様をまとめておく。
開発ディレクトリの作成
これから紹介するサンプルコードはvue-cliを使って開発ディレクトリを作っている。ディレクトリ構成と違う場合は、適宜読み替えてほしい。vue-cliの使い方については、以下の記事を参照。
. ├── index.html └── src ├── App.vue ├── main.js ├── pages │ ├── Detail.vue │ └── Index.vue ├── router // <-- vue-routerの定義 │ └── index.js └── store // <-- Vuexの定義 └── index.js
vuex-router-syncをインストールする
Vuexとvue-routerのバージョンが2.0以上の場合は、「@next」を付けて最新版を取得する。
# Vuexとvue-routerをインストール
$ npm i -S vuex vue-router
# vuex-router-syncの最新バージョンをインストール
$ npm i -S vuex-router-sync@next
vuex-router-syncを使う
はじめに、vue-routerの定義を「./router/index.js」に書く。
// ./router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{
path: '/',
name: 'index',
component: require('../pages/Index.vue')
},
{
path: '/detail/:id',
name: 'detail',
component: require('../pages/Detail.vue')
}
]
})
export default router
今回は名前付きルートで定義しているが、nameはあってもなくても良い。
次に、Vuexの定義を「./store/index.js」に書く。
// ./store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
list: [
{ id: 1, title: 'title-1' },
{ id: 2, title: 'title-2' },
{ id: 3, title: 'title-3' },
{ id: 4, title: 'title-4' },
{ id: 5, title: 'title-5' }
]
}
const actions = {
done ({ state, commit }) {
// state.routeでアクセス可能
console.log('actions:', state.route.params.id)
commit('done')
}
}
const mutations = {
done (state) {
// state.routeでアクセス可能
console.log('mutations:', state.route.params.id)
}
}
const getters = {
list: state => state.list,
detail: state => {
// state.routeでアクセス可能
return state.list.find(l => l.id.toString() === state.route.params.id.toString()) || {}
}
}
export default new Vuex.Store({
state,
actions,
mutations,
getters
})
vuex-router-syncを使うことでstate.route.path, state.route.params, state.route.queryにアクセスできる。(通常はできない)
次に、メイン処理を「./main.js」に書く。
// ./main.js
import Vue from 'vue'
import App from './App'
import { sync } from 'vuex-router-sync'
import router from './router'
import store from './store'
sync(store, router)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
sync(store, route)でvuex-router-syncを使えるようになる。
最後に、Vueファイルを書く。
まずはメインとなる「App.vue」
このファイルを中心にページやコンポーネントを読み込むことになる。
<!-- ./App.vue -->
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
次に「Index.vue」。これはstore.state.listを一覧表示している。
リンクをクリックすることで「/detail/:id」のページに移動する。
<!-- ./pages/Index.vue -->
<template>
<ul>
<li v-for="l in list">
<router-link :to="{ name: 'detail', params: { id: l.id }}">{{ l.title }}</router-link>
</li>
</ul>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['list'])
}
}
</script>
最後に「Detail.vue」
ここには「/detail/:id」のidを受け取り、その詳細情報を表示するという内容。
1つは、vue-routerの標準機能として取得できるthis.$route.params.idをつかって、this.$store.state.listから取得する方法。
他方、vuex-router-syncで取得できるstate.route.params.idを使って、gettersから取得する方法を書いている。
<!-- ./pages/Detail.vue -->
<template>
<div>
<h2>{{ detail.title }}</h2>
<h3>vue-routerの機能</h3>
<h4>$route</h4>
<pre>{{ $route | formatJSON }}</pre>
<h4>$store.state.listを$route.params.idで取得(createdで実行)</h4>
<pre>{{ dataDetail | formatJSON }}</pre>
<h3>vuex-router-syncの機能</h3>
<h4>$store.state.route</h4>
<pre>{{ $store.state.route | formatJSON }}</pre>
<h4>$store.state.routeを使ってgetters.detailから取得</h4>
<p>{{ detail | formatJSON }}</p>
<button @click="done">Done</button>
<router-link to="/">戻る</router-link>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
data () {
return {
dataDetail: []
}
},
created () {
this.dataDetail = this.$store.state.list.find(l => l.id.toString() === this.$route.params.id.toString())
},
computed: {
...mapGetters(['detail'])
},
methods: {
...mapActions(['done'])
},
filters: {
formatJSON (val) {
return JSON.stringify(val, null, ' ')
}
}
}
</script>
vue-routerのデフォルト機能としてthis.$routeでroute情報にアクセスすることができるので、vuex-router-syncを使うメリットが理解できなかったが、サンプルコードを書いたことでようやく理解できた。
また、/(ルート)から/detail/1に移動したときのstate.route.params.idはNumber型なのに対し、/detail/1でリロードするとString型に変わってしまう。
なのでサンプルコードではtoStringをしているのだが、ホントはすべてString型でやるべきなんじゃないかと思う。
ちなみにNumber型になる理由は、「<router-link :to="{ name: 'detail', params: { id: l.id }}">」の「l.id」がNumber型のため。
これを実際に動かすと、以下のようになる。
vuex-router-syncについてより詳しく
サンプルコードを載せて、ひととおり動くところまで見ていただいたところで、vuex-router-syncについてもうちょっと深く掘り下げてみる。
vuex-router-syncを使うことのメリットは、store.state.routeでroute情報にアクセスできることに尽きる。vue-routerだけだとthis.$routeが参照できる範囲でしか値を取得できない。
ちなみに以下のコードが、this.$routeの内容とthis.$store.state.routeの内容。
//console.log(this.$route)
{
"name": "detail",
"meta": {},
"path": "/detail/1",
"hash": "",
"query": {},
"params": {
"id": 1
},
"fullPath": "/detail/1",
"matched": [
{
"path": "/detail/:id",
"components": {
"default": {
"computed": {},
"methods": {},
"filters": {},
"__file": "/Users/s-shinoda/personal/vuex-router-sync/src/pages/Detail.vue",
"staticRenderFns": [],
"beforeCreate": [
null
],
"beforeDestroy": [
null
],
"_Ctor": {}
}
},
"instances": {},
"name": "detail",
"meta": {}
}
]
}
// console.log(this.$store.state.route)
{
"name": "detail",
"path": "/detail/1",
"hash": "",
"query": {},
"params": {
"id": 1
},
"fullPath": "/detail/1"
}
注意点としては、store.state.routeはimmutable(不変なオブジェクト)なので、変更することができない。もしURLやクエリパラメータをコード内で更新したい場合は、$router.go()を使って対応する。たとえば、$router.go({ query: {...} })のようにすればクエリパラメータを更新することができる。
以上
written by @bc_rikko
0 件のコメント :
コメントを投稿