4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

NuxtJS+TypeScriptの開発記録 (2) vuex-class-componentの導入

Posted at

NuxtJS+VuexとTypeScriptを組み合わせるときの不満は主に:

  • this.$storeは型情報がない=補完もチェックも効かない
  • ミューテーションを'increment'みたいに文字列でcommitする=補完もチェックも効かない

これじゃあせっかくTypeScriptにする意味が半減してしまう。そこで、公式ページに紹介されていたvuex-class-componentを使ってみる。1

サンプル(ライブラリ導入前)

よくあるシンプルなカウンターのサンプル。

store/counter.ts
import { MutationTree } from 'vuex';

// 本当はクラスで書きたい……
export const state = () => ({
  count: 0
});

export type RootState = ReturnType<typeof state>;

export const mutations: MutationTree<RootState> = {
  increment: state => state.count++
};
store/index.ts
export const state = () => ({});
components/counter.vue
<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="increment()">
      +
    </button>
  </div>
</template>
<style></style>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';

@Component
export default class Counter extends Vue {
  public get count(): number {
    return this.$store.state.counter.count; // $storeはany 補完やチェックが効かない
  }

  public increment(): void {
    this.$store.commit('counter/increment'); // 文字列なので補完やチェックが効かない
  }
}
</script>

ライブラリ導入

$ yarn add -D vuex-class-component

ストア | Nuxt TypeScriptによると、バージョン2.2.1現在、New APIの実装はまだNuxtJSとの互換性が完璧ではなく、今回はOld APIを使う必要がありそうだ。

store/counter.ts
import { Module, mutation, VuexModule } from 'vuex-class-component';

// クラスで書けた!
@Module({ namespacedPath: 'counter', target: 'nuxt' })
export class CounterStore extends VuexModule {
  public count = 0;

  @mutation increment(): void {
    this.count++;
  }
}
store/index.ts
import Vuex from 'vuex';
import { CounterStore } from './counter';

export const store = new Vuex.Store({
  modules: {
    counter: CounterStore.ExtractVuexModule(CounterStore)
  },
  strict: false // 下記のVXMを使うのに必要
});

export interface VXM {
  counter: CounterStore;
}

// ストアに型情報つきでアクセスできるプロキシオブジェクト
export const vxm: VXM = {
  counter: CounterStore.CreateProxy(store, CounterStore)
};

上記のvxmを使うと、以下のように型情報つきでストアにアクセスできる。

counter.vue(script部分のみ抜粋)
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import { vxm } from '@/store';

@Component
export default class Counter extends Vue {
  public get count(): number {
    return vxm.counter.count; // 補完とチェックが効く!
  }

  public increment(): void {
    vxm.counter.increment(); // メソッドとして呼び出せる!
  }
}
</script>

ストアに新しいクラスを追加するたびにstore/index.tsstoreVXMvxmを修正する必要はあるが、まぁこれは許容範囲とする。

VXMをNuxtJSのプラグイン化する

コンポーネントで毎回vmxをインポートするのは面倒なので、NuxtJSのプラグイン機能を使って、this.$storeと同じように、this.$vxmでアクセスできるようにしてみる。

plugins/vmx.ts
import Vue from 'vue';
import { vxm } from '@/store';

Vue.prototype.$vxm = vxm;
nuxt.config.ts(抜粋)
-  plugins: [],
+  plugins: ['~/plugins/vxm.ts'],

これでプラグインの追加は完了なのだが、Vueインターフェースには$vxmの情報がないため、TypeScript的には使えない。
そこで、前回追加したvue-shim.d.tsファイルにvxmの型定義を追加し、以下のようにする。

vue-shim.d.ts
import Vue from 'vue';
import { VXM } from './store';

declare module '*.vue' {
  export default Vue;
}

// 既存の型定義に追加する記法
declare module 'vue/types/vue' {
  interface Vue {
    $vxm: VXM;
  }
}

コンポーネント側は以下のように修正。

counter.vue(script部分のみ抜粋)
 <script lang="ts">
 import { Vue, Component } from 'vue-property-decorator';
-import { vxm } from '@/store';

 @Component
 export default class Counter extends Vue {
   public get count(): number {
-    return vxm.counter.count; // 補完とチェックが効く! 
+    return this.$vxm.counter.count; // 補完とチェックが効く! 
   }

   public increment(): void {
-    vxm.counter.increment(); // メソッドとして呼び出せる! 
+    this.$vxm.counter.increment(); // メソッドとして呼び出せる! 
   }
 }
 </script>

これで、this.$storeと同じような感覚で使える型情報つきのthis.$vxmが手に入った。
とりあえずこの構成で開発してみる。

参考にしたページ

  1. vuex-module-decoratorsの方が有名なようなのだが、Usageに@Action({ commit: 'increment' })と文字列で指定してるところがあったので、希望に沿わなかった。

4
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?