概要
VuexとTypeScriptの相性はとても悪いと言われてますがvuex-module-decoratorsとnuxt-property-decoratorを使用して型定義をしていきます。
はじめに
VuexとTypeScriptの書き方を調べると結構色々な書き方が出てきます。
そしてVue.jsとNuxt.jsで少しVuexの書き方も違うのでこんがらがる人も多いと思います。
個人的な意見ですが、現時点ではオブジェクト指向でclassベースでかける
vuex-module-decorators
nuxt-property-decorator
を使うのが最適だと思います。
ライブラリーをインストール
npm i --save vuex-module-decorators
npm i --save nuxt-property-decorator
結構しょっぱなでここでエラーが出る人もいるみたいですが、その場合はwebpackのバージョンをダウングレードすると治ることが多いです。
nuxt.jsのVuex
Vue.jsではVuexをインストールする必要がありましたが、Nuxt.jsではVuexが元々組み込まれています。
ルートディレクトリのstore配下にmodule名.jsを作成することによってmoduleが自動的に生成される仕組みになっています。
今回は簡単なcountUpを実装しTypeScriptを使用していくので
CountStore.tsとします。
stateとgetters
touch store/CountStore.ts
CountStore.tsのファイルを作成し、
import { Module, VuexModule } from 'vuex-module-decorators'
@Module({
name: "CountStore",
namespaced: true,
stateFactory: true
})
export default class CountStore extends VuexModule {
// 基本的にstateには直接アクセスしない方が良いのでこちらはprivateにしています
private _count: number = 1
// こちらがgettersの役割、stateからではなくgettersでアクセスしましょう
public get count(): number {
return this._count
}
}
まずここではvuex-module-decoratorsのimportが必要になります。
@Module({
name: "CountStore",
namespaced: true,
stateFactory: true
})
Moduleデコレーターを記述することによって
export function createStore() {
return new Vuex.Store({
modules: {
CountStore,
}
})
}
このようにモジュールを作成したことと同じようになります。
stateFactory: trueとすることでNuxtがモジュールモードでstoreを作ってくれます。
次にこちらを使えるようにしていきます。
mkdir utils
touch utils/store-accessor.ts
touch store/index.ts
ファイルを2つ作成します。
import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store)
export const plugins = [initializer]
export * from '~/utils/store-accessor'
こちらはドキュメントに習って記述していきます。
https://github.com/championswimmer/vuex-module-decorators
基本的にindex.tsは一度作成したら触る事はありません。
store-accessor.tsで基本的に作成していきます。
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import CountStore from '~/store/CountStore'
let countStore: CountStore
function initialiseStores(store: Store<any>): void {
countStore = getModule(CountStore, store)
}
export { initialiseStores, countStore }
こちらでgetModuleを定義しCountStoreの情報を取得し、exportしています。
結構複雑ですね、、、、
この辺りはおまじない程度に考えておきましょう。
データの受け取り
<template>
<div>
<h1>{{ countNumber }}</h1>
</div>
</template>
<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator"
import { countStore } from "~/utils/store-accessor"
@Component
export default class Index extends Vue {
get countNumber(): number {
return countStore.count
}
}
</script>
基本的にはこれで受け取れます。
import { countStore } from "~/utils/store-accessor"
でcountStoreの情報をimportしgetterにはcountStore.定義したgetterでアクセスすることができます。
Mutaion
CountStore.tsを書き換えましょう。
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
@Module({
name: "CountStore",
namespaced: true,
stateFactory: true
})
export default class CountStore extends VuexModule {
// 基本的にstateには直接アクセスしない方が良いのでこちらはprivateにしています
private _count: number = 1
// こちらがgettersの役割、stateからではなくgettersでアクセスしましょう
public get count(): number {
return this._count
}
// Mutaionデコレーターを定義
@Mutation
increment(sumCount: number): void {
this._count += sumCount
}
}
Mutationはデコレーターで定義していきます。
Module,Mutation、これから紹介するAcrionsはデコレーターで定義なのにgettersだってgetで定義するのがちょっとわかりづらいですね笑
受け取る
index.vueを書き換えましょう
<template>
<div>
<h1>{{ countNumber }}</h1>
<button @click="countUp">カウントアップ</button>
</div>
</template>
<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator"
import { countStore } from "~/utils/store-accessor"
@Component
export default class Index extends Vue {
private get countNumber(): number {
return countStore.count
}
// 追加部分
private countUp(): void {
countStore.increment(1)
}
}
</script>
Mutationはメソッドとして定義します。
これもcountStore.定義したMutationでうけとれます。
Actionの定義
Mutationは同期的な処理しか記述できないのに対してActionは非同期処理が扱えます。
また書き換えていきましょう!
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
@Module({
name: "CountStore",
namespaced: true,
stateFactory: true
})
export default class CountStore extends VuexModule {
// 基本的にstateには直接アクセスしない方が良いのでこちらはprivateにしています
private _count: number = 1
// こちらがgettersの役割、stateからではなくgettersでアクセスしましょう
public get count(): number {
return this._count
}
// Mutaionデコレーターを定義
@Mutation
increment(sumCount: number): void {
this._count += sumCount
}
// 非同期処理を定義
@Action
countAction() {
setTimeout((): void => {
this.increment(1)
}, 3000)
}
}
@Actionでデコレーターを定義することで非同期処理を書くことができます。
受け取り
受け取る際は今まで通り
<template>
<div>
<h1>{{ countNumber }}</h1>
<button @click="countUp">カウントアップ</button>
<button @click="countUpAction">3秒後にカウントアップ</button>
</div>
</template>
<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator"
import { countStore } from "~/utils/store-accessor"
@Component
export default class Index extends Vue {
private get countNumber(): number {
return countStore.count
}
private countUp(): void {
countStore.increment(1)
}
//受け取り
private countUpAction(): void {
countStore.countAction()
}
}
</script>
Actionもメソッドとして定義して受け取ります。
お疲れさまでした!!