【Composition API】StoreパターンでVuexを使わずに状態管理をする


はじめに

こんにちは。@chan_kakuです!今回はVue Advent Calendar 2019 11日目の記事です。

みなさんComposition API使っていますか??今年のVue Advent CalendarはComposition APIの話がとても多いですね!
ここからみなさんがComposition APIに非常に関心があることがわかります。
今回は来たるVue 3.0で入るComposition APIをつかってVuexを使わずにStoreパターンを利用して簡易状態管理をしてみたいと思います!

そもそもVuexって何?

今更ですが、初めてみたという方のために説明を入れておきます。
Vuexの公式ドキュメントをみてると最初にこのように書いてあります

Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。 これは予測可能な方法によってのみ状態の変異を行うというルールを保証し、アプリケーション内の全てのコンポーネントのための集中型のストアとして機能します。 また Vue 公式の開発ツール拡張と連携し、設定なしでタイムトラベルデバッグやステートのスナップショットのエクスポートやインポートのような高度な機能を提供します。

Vuexをよく使うパターンとしては、親子関係ではなく、兄弟関係のコンポーネント間で何かしらの状態を渡したいときなんかに使われます。
ただ、公式ドキュメントにはこのようにも書いてあります。

Vuex は、共有状態の管理に役立ちますが、さらに概念やボイラープレートのコストがかかります。これは、短期的生産性と長期的生産性のトレードオフです。
もし、あなたが大規模な SPA を構築することなく、Vuex を導入した場合、冗長で恐ろしいと感じるかもしれません。そう感じることは全く普通です。あなたのアプリがシンプルであれば、Vuex なしで問題ないでしょう。単純な ストアパターン が必要なだけかもしれません。しかし、中規模から大規模の SPA を構築する場合は、Vue コンポーネントの外の状態をどうやってうまく扱うか考える絶好の機会です。Vuex は自然な次のステップとなるでしょう。

つまり、公式的にはそこまで大きくないアプリケーションであるにもかかわらず、単に状態管理したいだけのために入れるのはナンセンスで、それなりの規模のアプリケーションで使うことをおすすめしています。

そこで公式ドキュメントに乗っている通りStoreパターンを利用して兄弟関係のコンポーネント同士で状態管理をしてみたいと思います。

早速Composition APIで状態管理してみる

作るもの

導入の前に今から作るもののイメージがあった方が作りやすいと思うので載せておきます

このようにtext-boxに入力したテキストをsetボタンを押すことでstoreにセットされ、getボタンを押すことでstoreの状態を取得することができいます。

導入

まずはいつものごとくvue-cliを使ってプロジェクトを利用していきます

$ vue create adventCalendar

オプションはよしなに好きなものを選んでください

その後、Composition APIをVue 2.xでも利用できる @vue/composition-api を入れていきます

$ yarn add @vue/composition-api

その後先ほどvue-cliで作成したプロジェクトの/src/main.jsに以下のように記述することでComposition APIを利用することができます。

main.js
import Vue from 'vue'
import App from './App.vue'
import VueCompositionApi from '@vue/composition-api';// 追加した箇所

Vue.use(VueCompositionApi);// 追加した箇所

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

状態管理

ここからようやくStoreパターンを使った状態管理に入ります
storeパッケージを切って、その配下にstore.jsを作成します

store.js
export let store = {
    state: {
        message: 'this is first message'
    },
    getMessage() {
        return this.state.message
    },
    setMessage(newVal) {
        this.state.message = newVal
    }
}

続いて、component側を触っていきます
/src/components/HelloWorld.vueを以下のように修正していきます

HelloWorld.vue
<template>
  <div>
    <textarea cols="30" rows="10" v-model="state.privateMessage"></textarea>
    <p>グローバルメッセージ:{{state.globalMessage}}</p>
    <button @click="setMessage">set</button>
    <button @click="getMessage">get</button>
  </div>
</template>


<script>
import { reactive, onMounted } from '@vue/composition-api'
import { store } from '@/store/store.js'

export default {
  name: 'HelloWorld',
  setup() {
    onMounted(() => {
      getMessage()
    })

    const state = reactive({
      privateMessage: '',
      globalMessage: ''
    })

    function getMessage() {
      state.globalMessage = store.getMessage()
    }

    function setMessage() {
      store.setMessage(state.privateMessage)
    }

    return {
      state,
      getMessage,
      setMessage
    }
  }
}
</script>

Composition APIは基本的にsetUp()内で使っていくことになります。
onMountedはVue 2.xまでのライフサイクルであるmountedと同じものになります。他にもいままで同じようにライフサイクルをフックすることができますが一部使えなくなっているものがありますのでご注意ください。
reactiveはVue 2.6で追加されたVue.observable() と同じものでオブジェクトをリアクティブにしてくれるAPIです。
コンポーネント側ではこのようにstoreの状態(state)のgetter/setterを利用して状態を取得/変更していきます。
template側で利用したいオブジェクト、関数等がある場合はreturnで返してあげることで利用することができます。
ここまでみていただたとおり、従来の書き方で連発していたthisがなくなっています。
初めて使ったとき個人的にはこれだけでもかなり嬉しく感じました。

今回は兄弟コンポーネントでの状態共有をしていきたいので、兄弟コンポーネントを作っていきます。
今回はサンプルなので、HelloWorld.vueをコピーしたHelloWorld2.vueを作成し、App.vueでimportするようにしました

これで完成です!!

感想

ちょっとしたアプリケーションで状態管理をしたいだけの場合、このようにStoreパターンを利用することで状態管理をすることができました。
Vuexを使うかどうかの判断は難しいですが、個人的な意見としては、いらないものはできるだけ省きたいのでまずはStoreパターンでやってみる等でいいのかなと思ってます。
Composition APIの方は今回の例ではあまり旨味を出せていませんが、もう少し複雑なものになってくると、やりたいこと単位で分けてかけるので、レビューとかでもみやすくなってとてもいい感じだと思ってます!!
みなさんもVue 3.0がくる前にComposition APIにチャレンジしてみましょう!!

その他

Composition APIの書き方にまだ慣れていないため、もっとこうした方がいい等の意見がありましたらコメントいただけると助かります。

参考