19
8

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 5 years have passed since last update.

nuxt+typescript+vuex-class-component+vuexfireに成功しました

Posted at

私の求めていたこと

await this.$store.dispatch("hoge", 1) // type-safeではないし、補完も何もないしtypoすらわからない。素のvueやvuexの最も嫌いなところ。
@Component
class Xxx extends Vue {
  niceStore = ...
  
  async hoge() {
    await niceStore.hoge(1) // js(ts)の普通のメソッドして扱われ補完も聞くしdocも出るしtypoしたら起こってくれるし引数のチェックもしてくれる
  }
}

それが可能なライブラリを探した。

https://github.com/ktsn/vuex-class : これは公式っぽいが、こういうことではない。これはただのショートハンド。定義でtype-safeにするものではない。

https://github.com/championswimmer/vuex-module-decorators : だいたい私の悩みを解決してくれる。vuex-classを除けばgithubのstarも圧倒的に多い。でもaction周りのUsageがどうも好きになれなかった。vuexfireとも組み合わせられる気が全くしない。

https://github.com/michaelolof/vuex-class-component : これである。READMEを見て欲しい。これこそが求めていたものである。ただサードパーティのライブラリであるしvuexのバージョンが変わったら動かなくなるかもしれない。しかし、勇気を出して採用してみることにした。

まずはvuexfireのことを考えず、nuxt+vuex-class-componentを動かす

nuxt+vuex-class-component

前提: https://ja.nuxtjs.org/guide/typescript/ は済ませておく

早速npm install vuex-class-component したいところだが、まずこれが地雷
この記事を書いている時点でnpmのlatestは1.6.0。1.6.0では私はなぜか動かせなかった。この記事を書いている時点でのgithubのmasterHEAD では1.7.0開発中なのでこれを利用する

> npm i -S https://github.com/michaelolof/vuex-class-component
package-lock.json
    // 私はこのコミットのバージョンが入りました
    "vuex-class-component": {
      "version": "git+https://github.com/michaelolof/vuex-class-component.git#c9c3f69ba0620f788df156787cde80d418762b14",

(※ npmで1.7.0が公開されたら普通にnpmで^1.7.0で良いと思います)

次に(この記事を書いている時点の)READMEを参考にセットアップしたいですが、だいぶ簡略ですし、もう少しいろいろ書かないと動きません。

(※(私はものすごく英語が苦手ですが)issueで作者にドキュメントを要望していますので、もしよければ応援していただければと思います。ついでに、このライブラリ、イカス!と思ったらstarを入れて作者を応援しましょう)

では、動いたっぽい構成の紹介です(時間がないのでうろ覚えで雑に書きます。間違ってたらごめんなさい)

  • store/
    • index.ts
    • news.ts
  • pages/
    • index.vie
store/index.ts
export const state = () => ({}) // なにかしらないとnuxt的にだめっぽい(?)
store/posts.ts
import { Module, VuexModule, mutation, getter } from 'vuex-class-component'

export interface Post {
  title: string
}

@Module({ namespacedPath: 'news/', target: 'nuxt' })
export class NewsStore extends VuexModule { // extends VuexModleはDocumentでは書いてないけど必須っぽい

  @getter posts: Post[] = [ // getterがないと公開されないかも。vuexのgetterとは似て非なる意味合いかもしれない。外から読みたいstateにもつけないとだめ(?)
    { title: "title1" }
  ]
}

export default NewsStore.ExtratVuexModule(NewsStore) // Documentには書いていないけどこれは必須っぽい
pages/index.vue
<template>
  <div>
    <div v-for="post in posts">{{ post.title }}</div>
  </div>
</template>
<script lang="ts>
import { Component, Vue } from 'vue-property-decorator'
import { NewsStore, Post } from '~/store/news'

@Component
export default class extends Vue {
  news = NewsStore.CreateProxy(this.$store, NewsStore)

  get posts(): Post[] {
    return this.news.posts
  }

  // mustaionもactionもasyncも行けている感じにかけるので詳しくは
  // https://github.com/michaelolof/vuex-class-component/blob/master/README.md)へ
</script>

うごきました。

+ Vuexfire

前提としてfirestore上で

  • posts
    • title: string

というデーター構造を作っているとします。
firebase sdk webの導入は済んでいるとします。

まずvuexfireのよくある地雷ですが、
https://github.com/posva/vuexfire は3.0.0-alpha5までの古いやつで、最新のものは、 https://github.com/vuejs/vuefire/tree/master/packages/vuexfire ですので、READMEを参照するときは注意を。使い方が大きく変わっています。

npm i -S vuexfire

package-lock.json
// 私はvuexfireの3.0.0-alpha.18が入りました
    "vuexfire": {
      "version": "3.0.0-alpha.18",

一番難しいのがfirestoreActionをどう組み込むかですが、

こいつが厄介でvuex-class-component上で普通に利用することができないです。
thisが素のmoduleであることを強要してきます。

ですので、rootのstoreにvuexfire系のactionを定義。
submoduleでvuex-class-componentの定義。
そしてrootのstoreのactionから、submoduleのstateに対してbindするということをすると、成功しました。

日本語が下手で何を言っているかわからないと思うのでサンプル書きます!

  • store/
    • index.ts
    • news.ts
  • pages/
    • index.vue
store/index.ts
import firebase from 'firebase/app'
import 'firebase/firestore'
import { firestoreAction, vuexfireMutations } from 'vuexfire'

export const state = () => ({})

export const mutations = {
  ...vuexfireMutations
}

export const actions = {
  setNewsPostsRef: firestoreAction(({ bindFirestoreRef }, { ref }) => {
    bindFirestoreRef('news.posts', ref) // submoduleのstateに無理やりbind。これしか無かった。これがミソ。この記事の一番言いたいところです
  })
}

// 本筋ではないですが、私はよく、アプリ起動時にvuexのinitializeタイミングでfirestoreのbindを行います。
// spaじゃないとだめかも知れません。
// 他に同じようなことをやっている人があまりいないので、このへんはお好みです。
// 普通にindex.vueのcreateなどで呼ぶのでもいいと思います
export const plugins = [
  (store) => {
    const db = firebase.firestore()
    store.dispatch('setNewsPostsRef', { ref: db.collection('posts') })
  }
]
store/news.ts
// 前述のままで良い
pages/index.vue
// 前述のままで良い

です!!
vuex-class-component自体の導入に際してnuxt.configなどを触る必要は一切ありませんでした。

蛇足

この記事を書いた時点でググっても誰もやってない(or成功していない)感じだったので、書きました。
まとまって無くてごめんなさい(今は諸事情で時間がないので、がーっと書きなぐっている状態です)

https://www.google.co.jp/search?q=vuex+class+vuexfire → 全然ない(もしくはvuex-class(コレジャナイやつ)という情報のみ)

https://www.google.co.jp/search?q=vuex-class-component+vuexfire → 全然ない

https://www.google.co.jp/search?q=vuex-module-decorators+vuexfire → 全然ない

各種バージョン

cat .node-version 
v12.6.0
npm -v
6.10.0
    "nuxt": {
      "version": "2.8.1",
    "vuex": {
      "version": "3.1.1",
    "ts-node": {
      "version": "8.3.0",
19
8
1

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
19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?