Piniaの使い方

Piniaの使い方
Vue3Pinia
2022年11月11日 更新

紹介

PiniaはVue3シリーズでおすすめられたステータス管理のライブラリです。

そのうみ親はVue.jsのチームです、主に、VuexをComposition-API風に作り直したものです。 つまり、PiniaはTypescriptとの相性高いです、Vue3で使うをおすすめですが、Vue2では本領発揮できないかと思います。

以下はVuex3.x/4.xの区別です。

| 項目 | Vuex3.x/4.x | Pinia | | ---- | ---- | ---- | | mutations | ある | ない | | async | できない | できる | | typescript | イマイチ | 最高 | | nested | ある | ない、全部Store | | plugin | ない | 作れる | | 大きさ | 普通 | 小さい、2kbくらいできる |

環境構築

npm install pinia

yarn add pinia

上記ライブラリ追加後、main.tsmain.jsに下記を追記によって、piniaを発動。

import { createPinia } from 'pinia'

const pinia = createPinia()
app.use(pinia)

手順

Storeを作る:

  • 1.src/storesにで、counter.tsを作成
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const powerCount = computed(() => count.value * count.value)
  function add() {
    count.value++
  }

  return { count, powerCount, add }
})


  • 2.コンポーネントでcounterを使う
<script setup>
import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()
counterStore.count++
counterStore.$patch({ count: counterStore.count + 1 })
counterStore.add()
</script>

<template>
  <div>{{ counterStore.count }}</div>
  <div>{{ counterStore.powerCount }}</div>
</template>

  • 3.counterのStore中身をリストアップ
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()
const { count, powerCount } = storeToRefs(counterStore)
</script>

<template>
  <div>{{ count }}</div>
  <div>{{ powerCount }}</div>
</template>

  • 4.counterのStore中身を変更する、$patchをおすすめです。
<script setup>
import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()
counterStore.$patch({
  count: counterStore.count + 1,
  name: 'Reinf0rce',
})
</script>

  • 5.counterのStore中身の変更を監視します、$subscribeをおすすめです。
<script setup>
import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()
counterStore.$subscribe((mutation, state) => {
  localStorage.setItem('counter', JSON.stringify(state))
})
</script>


// main.tsに追加したら全体になります

import { watch } from 'vue'
import { createPinia } from 'pinia'

const pinia = createPinia()
watch(
  pinia.state,
  (state) => {
    // 内容変更した時、データをローカルストレージに保存
    localStorage.setItem('piniaState', JSON.stringify(state))
  },
  { deep: true }
)
  • 6.Storeの実態をアクセスできます、基本的にthisを使って、現在のStoreの実態(Counter)をアクセスできます。
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    powerCount(state) {
      return state.count * state.count
    },
    powerPlusOne() {
      return this.powerCount + 1
    }
  }
})

  • 7.現在のStoreの実態(Counter)ではなく、そのほかをアクセスしたい場合、getterを使おう
import { defineStore } from 'pinia'
import { useOtherStore } from './otherStore'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 1
  }),
  getters: {
    composeGetter(state) {
      const otherStore = useOtherStore()
      return state.count + otherStore.count
    }
  }
})

  • 8.getterを使って、関数でパラメータを受け取る
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    users: [{ id: 1, name: 'Rein'}, {id: 2, name: 'f0rce'}]
  }),
  getters: {
    getUserById: (state) => {
      return (userId) => state.users.find((user) => user.id === userId)
    }
  }
})

// コンポーネントで

<script setup>
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()
const { getUserById } = storeToRefs(userStore)
</script>

<template>
  <p>User: {{ getUserById(2) }}</p>
</template>

  • 9.actionsで非同期を行います。
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({ userData: null }),
  actions: {
    async registerUser(login, password) {
      try {
        this.userData = await api.post({ login, password })
      } catch (error) {
        return error
      }
    }
  }
})

  • 9.その他のStoreにあるactionsで非同期を行います。
import { defineStore } from 'pinia'
import { useAuthStore } from './authStore'

export const useSettingStore = defineStore('setting', {
  state: () => ({ preferences: null }),
  actions: {
    async fetchUserPreferences(preferences) {
      const authStore = useAuthStore()
      if (authStore.isAuthenticated()) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated!')
      }
    }
  }
})
  • 10.プラグインを追加して、カスタマイズする
export function myPiniaPlugin(context) {
  // contextは使える
  // ...
  return {
    hello: 'world', // 为 state 添加一个 hello 状态
    changeHello() { // 为 actions 添加一个 changeHello 方法
      this.hello = 'pinia'
    }
  }
}

// 実際に使うところ
import { createPinia } from 'pinia'

const pinia = createPinia()
pinia.use(myPiniaPlugin)

その他

Storeに新しいステータスを直接追加する。

import { ref } from 'vue'

const globalSecret = ref('secret')
pinia.use(({ store }) => {
  // `secret` 関連は全Store対象でアクセス可能
  store.$state.secret = globalSecret
  store.secret = globalSecret
})

また、さらに、Storeにも新しいアクションを追加も可能です。

import { defineStore } from 'pinia'

export const useSearchStore = defineStore('search', {
  actions: {
    delay() {
      // ...
    },
  },

  debounce: {
    // 遅延 300ms
    delay: 300,
  }
})

// 全体効くので

import { createPinia } from 'pinia'
import { debounce } from 'lodash'

const pinia = createPinia()
pinia.use(({ options, store }) => {
  if (options.debounce) {
    // 遅延を全体のactionに効くようにする
    return Object.keys(options.debounce).reduce((debouncedActions, action) => {
      debouncedActions[action] = debounce(
        store[action],
        options.debounce[action]
      )
      return debouncedActions
    }, {})
  }
})

キャッシュ

Vuexではページをリフレッシュすると、入力するものは全部なくなる迷惑動作ありました。

Piniaはプラグインの活用によって、すぐに解決できるようになりました。

  • 1.npm i pinia-plugin-persistでライブラリを追加

  • 2.persistを全体のStoreで使えるようにする

import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'

const pinia = createPinia()
pinia.use(piniaPluginPersist)

  • 3.Storeにて、persistを効くように設定
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 1 }),
  // persistのキャッシュ機能
  persist: {
    enabled: true
  }
})

上記ステップによって、ユーザー入力するものはなくならないでしょう。