💬

Vue2 + pinia

2022/06/14に公開

vue2でもcomposition apiを入れていればpiniaを使えるということで、
早速実装してみました。

追記)composition apiを入れてなくても使えるようになっているみたいです。

公式のしたがってインストールから実装までやっていきたいと思います。
(typescriptにも対応していますが、この記事ではjavascriptを例に書いています)
https://pinia.vuejs.org/getting-started.html

Step 1 インストール

npm install pinia

main.js(ts)に追記します。

main.js
import Vue from "vue"
import App from "./App.vue"
import router from "./router"
import VueCompositionApi from "@vue/composition-api"
import vuetify from "./plugins/vuetify"
import { createPinia, PiniaVuePlugin } from "pinia" // 追加

Vue.use(VueCompositionApi)
Vue.use(PiniaVuePlugin) //追加
const pinia = createPinia()
Vue.config.productionTip = false

new Vue({
  router,
  vuetify,
  pinia, //追加

  render: function (h) {
    return h(App)
  },
}).$mount("#app")

Step 2 store定義

defineStoreでstoreを定義します。
第一引数は、storeのIDになるので、一意である必要があります。

/store/counterStore.js
import { defineStore } from "pinia"

export const useCounter = defineStore("counter", {
  //vueで言うdata()に値する:reactiveなデータ
  state: () => ({
    count: 0,
  }),
  //vueで言うcomputed()に値する
  getters: {
    doubledCount() {
      return this.count * 2
    },
  },
  //vueで言うmethods()に値する
  actions: {
    increment() {
      this.count++
    },
  },
})

基本構成は、state, getters, actionsの3つであり、
これはvueでいうdata(), computed(), methodsに値します。

命名規則について特に指定はないようですが、
gettersはcomputed()と同じ位置づけということなので、名詞がよさそうです。

step 3 storeを使う(基本の書き方)

storeファイルをimportして、この一行を書くだけで使えるようになります。

const store = useCounter()
sample.vue
<template>
  <div>
    <p>currentCount: {{ store.count }}</p>
    <v-btn @click="upCount">カウントアップ</v-btn>
  </div>
</template>

<script>
import { defineComponent } from "@vue/composition-api"
import { useCounter } from "@/store/counterStore.js"

export default defineComponent({
  setup() {
    const store = useCounter()
    
    //store.countでstateのプロパティ(count)にアクセスできる
    console.log("currentCount", store.count)
    
    //store.でdoubledCountのgetters(doubledCount)にアクセスできる
    console.log("doubledCount", store.doubledCount)
    
    const upCount = () => {
      //store.アクション名でstoreのアクションを使える
      store.increment()
    }

    return { store, upCount }
  },
})
</script>

注意点としては、分割代入↓を使うとreactive性が失われます。

const store = useCounter()
const {count} = store

reactive性を維持するためには、別の方法を使う必要があります。

sample.vue
<script>
import { storeToRefs } from 'pinia'
import { useCounter } from "@/store/counterStore.js"

const store = useCounter()
const { count, doubledCount } = storeToRefs(store)
</script>

Step 4 storeを使う(色々)

stateの値を変更する

sample.vue
<script>
import { defineComponent } from "@vue/composition-api"
import { useCounter } from "@/store/counterStore.js"

export default defineComponent({
  setup() {
    const store = useCounter()
    store.count = 3 //直接変更できる
    
    return { store }
  },
})
</script>

その他

global state検証

あるコンポーネントで変更した値が、ほかのコンポーネントでも変更されるか?

Counter1.vueとCounter2.vueにそれぞれcounterStoreを入れて試したところ、
しっかり、反映されていることが確認出来ました!

Discussion