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.ts
やmain.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
}
})
上記ステップによって、ユーザー入力するものはなくならないでしょう。