Vue.js、Nuxt.jsでのVuex(store)の使いドコロ

こんにちは、あかしぃです。

自分は本業、副業でVue.js(Nuxt.js)を使っています。

Vue.jsはReact.jsに比べると初心者でもとっつきやすい印象があり、自分の場合もReact.jsの独学は挫折しましたが、Vue.jsは独学でもなんとかなりました。

ただ、Vuex、いわゆるstoreの使い所は本当に難しいな。。。と感じていて、今でも試行錯誤しながら実装しています。(ベストプラクティスのようなものが公開されていない)。

そこで、自分のVuexの使いドコロについて以下で解説していこうと思います。Vuexの存在は知っているけど、いまいち使うタイミングがわからない・・・という人の参考になれば幸いです。

Vuexのメリット、デメリット

まず、Vuexを使うことにどんなメリット、デメリットがあるのかです。

主に体験談ベースになるので恐縮ですが、メリットとしては以下のようなものがあると感じています。

  • 親 → 子 → 孫コンポーネントのような、数個のコンポーネントからなる機能の状態管理ロジックを明確にできる
  • 同一親コンポーネントを持つ子コンポーネント間の状態管理をシンプルにできる
  • ロジックの一部を別ファイルに切り出せるので、1ファイルあたりの記述量がコンパクトになり、見通しがよくなる

コンポーネントの親子関係が複雑になると、それだけ状態管理が大変になります。たとえば親の持つdataを孫から更新しようとすると、子を挟んでemitを伝達することになり、このemitはどのコンポーネントのイベントを発火させるのか、というのが不明確になります。

また、子コンポーネント間の通信を行うには、子からemit → 親から違う子へpropという記述になり、dataが増えるにつれ、カオスになっていくのは避けられないような気がします。

よほど小さいアプリケーションならイベントバスでクリアできると思いますが、結局アプリが大きくなってきたときにイベントバスでは管理できなくなるでしょうから、それなら最初からVuexでいいのでは・・・ということになります。

 

次にVuexのデメリットについて。

  • 単純にコードの記述量、ファイルの数が多くなる
  • 型安全を導入するのが難しい?(自分はTypeScriptをあまり知らないのですが、このデメリットをよく聞きます)

Vuexを導入すると、storeディレクトリを作ることになり、その配下に数多くのファイルを作成することになります。結果、管理すべきファイル量、コード量が増えてしまいます。

あともう一つ良く聞くのが、Vuexは型安全の面で不安があるということ。自分はTypeScriptをそこまで使ったことがないので、このデメリットについてはあまりよくわからないのですが・・・。

ただ、TypeScriptの導入がどんどん盛んになっていることからすると、このデメリットはより大きくなっていくのかも、とは思います。

個人的Vuexの使いドコロ

最初に書いたように、Vuexのベストプラクティスのようなものはまだ公開されていないです(自分で調べた限り)

なので、自分の使い方がベストプラクティスという保証はどこにもありません。むしろ、アンチパターンだったりするかも・・・。ということで、あくまで一つの参考として捉えてもらえると幸いです。

子コンポーネント間で通信をする場合は、Vuexを使う

子 ← 親 → 子のようなコンポーネント構造をしていて、子コンポーネント間で同一のdataを使う場合はVuexを使います。具体例はこんな感じ。

Parent.vue

<template>
  <div class="form">
    <form />
  </div>
  <div class="message">
    <message />
  </div>
</template>

<script>
import Form from '@/components/Form.vue'
import Message from '@/components/Message.vue'

export default {
  components: {
    Form,
    Message
  }
}
</script>

FormコンポーネントとMessageコンポーネントを持つ親コンポーネントです。

Formコンポーネントはmessageをinputする役割を持ち、Messageコンポーネントでは入力されたmessageを表示させます。

この場合、Vuexを用いてFormコンポーネントでstate.messageを更新し、Messageコンポーネントでmessageをgettersで取得し、表示させる感じです。

このくらいの規模なら親コンポーネントにmessageをdataとして持たせ、emitとpropでそれらを更新、管理しても問題ないと思いますが、孫コンポーネントが登場したり、親コンポーネントと子コンポーネント間で共有するdataが増えてくると、メンテナンスが大変なことになります。

それなら最初からVuexで管理してしまえば、どれだけdataが増えようともその管理は基本的にstoreが担ってくれるので、コンポーネントファイルのメンテナンスを容易に保てます。

親とある子コンポーネントでのみ共有されるdataについては、propsやemitを使う

こちらも具体例を出すとこんな感じです。

Parent.vue

<template>
  <div class="btn">
    <button @click="openModal">Open</button>
  </div>
  <div class="modal">
    <modal
      message="message"
      @close="closeModal" />
  </div>
</template>

<script>
import Modal from '@/components/Modal.vue'

export default {
  components: {
    Modal
  },

  data() {
    return {
      message: 'Hello!',
      showModal: false
    }
  },

  methods: {
    openModal() {
      this.showModal = true
    },

    closeModal() {
      this.showModal = false
    }
  }
}
</script>

この親コンポーネントは子にモーダルコンポーネントを持っています。

そして子コンポーネントから伝達されるのはshowModalというdataを変更したい!という命令だけです。

これならわざわざVuexを使う必要なく、素直にcloseイベントをemitすれば、それで十分かなと思います。

また、親から渡したいdataもmessageのみで、このmessageはモーダルコンポーネント以外では使われていませんから、直接propとして渡してあげればいいんじゃないかなと。

まとめ

非常にシンプルな例だったので、ピンと来ない人もいるかもしれませんが、「子コンポーネント間でdataを共有したいときはVuexを使う」ということを頭に置いておけば、それなりに体裁の整ったstoreになるんじゃないかなと思います。

ただ、Vuexそのものを使わないという選択肢もあるので、まずはアプリの規模などを考慮して、Vuexを導入するかどうか検討することからスタートするのがいいかなと思いますね。

April 07, 2019 - posted by akashixi