LoginSignup
4
0

More than 3 years have passed since last update.

Nuxt.js + Vuex + TypeScriptで型定義

Posted at

概要

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のファイルを作成し、

store/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つ作成します。

store/index.ts
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で基本的に作成していきます。

utile/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しています。
結構複雑ですね、、、、
この辺りはおまじない程度に考えておきましょう。

データの受け取り

pages/index.vue
<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を書き換えましょう。

store/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を書き換えましょう

pages/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は非同期処理が扱えます。
また書き換えていきましょう!

store/CountStore.ts
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でデコレーターを定義することで非同期処理を書くことができます。

受け取り

受け取る際は今まで通り

pages/index.vue
<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もメソッドとして定義して受け取ります。

お疲れさまでした!!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0