Плагин Pinia — это функция, которая встраивается в жизненный цикл хранилища и получает доступ к его внутреннему состоянию, действиям и метаданным. Плагины позволяют расширять поведение всех или отдельных store без дублирования кода и без изменения их исходной логики.
На уровне архитектуры плагин — это промежуточный слой между созданием store и его использованием в приложении. Он может:
Плагины регистрируются на уровне экземпляра Pinia и автоматически применяются ко всем store.
import { createPinia } from 'pinia'
const pinia = createPinia()
pinia.use((context) => {
// логика плагина
})
app.use(pinia)
Каждый плагин — это функция, принимающая контекст плагина, который содержит информацию о store и инструменты для взаимодействия с ним.
Контекст, передаваемый в плагин, имеет следующую структуру:
{
pinia, // экземпляр Pinia
app, // Vue-приложение
store, // текущий store
options // опции store
}
Ключевой объект, представляющий конкретное хранилище. Через него доступно:
store.$id — идентификатор store;store.$state — реактивное состояние;store.$patch() — атомарное изменение состояния;store.$subscribe() — подписка на мутации;Плагин может расширять store, добавляя новые свойства или функции. Это делается путём возврата объекта из плагина.
pinia.use(() => {
return {
createdAt: Date.now()
}
})
Теперь каждый store будет иметь свойство createdAt.
Добавление методов:
pinia.use(() => ({
reset() {
this.$reset()
}
}))
Метод reset становится частью API каждого store и
работает в его контексте.
Не всегда требуется применять плагин ко всем store. Условие можно
задать на основе store.$id или пользовательских опций.
pinia.use(({ store }) => {
if (store.$id !== 'user') return
return {
isUserStore: true
}
})
Использование кастомных опций store:
export const useCartStore = defineStore('cart', {
state: () => ({}),
enableLogger: true
})
pinia.use(({ options, store }) => {
if (!options.enableLogger) return
store.$subscribe((mutation, state) => {
console.log(mutation, state)
})
})
Pinia предоставляет механизм подписки на мутации состояния внутри плагина.
pinia.use(({ store }) => {
store.$subscribe((mutation, state) => {
console.log(`[${store.$id}]`, mutation)
})
})
Типы мутаций:
direct — прямое изменение состояния;patch object — изменение через
$patch({});patch function — изменение через
$patch(fn).Подписка полезна для логирования, аналитики и синхронизации.
Каждое действие можно обернуть, чтобы выполнить код до или после его вызова.
pinia.use(({ store }) => {
const originalActions = store.$actions
Object.keys(originalActions).forEach((actionName) => {
const original = store[actionName]
store[actionName] = async function (...args) {
console.log('before', actionName)
const result = await original.apply(this, args)
console.log('after', actionName)
return result
}
})
})
Этот подход применяется для:
Классический пример использования плагина — сохранение состояния в
localStorage.
pinia.use(({ store }) => {
const key = `pinia-${store.$id}`
const saved = localStorage.getItem(key)
if (saved) {
store.$patch(JSON.parse(saved))
}
store.$subscribe((_, state) => {
localStorage.setItem(key, JSON.stringify(state))
})
})
Особенности реализации:
$patch сохраняет реактивность;Для корректной типизации расширений store используется декларативное расширение интерфейсов.
import 'pinia'
declare module 'pinia' {
export interface PiniaCustomProperties {
createdAt: number
reset(): void
}
}
После этого свойства и методы, добавленные плагином, будут доступны в IDE и проверяться компилятором.
$subscribe.Pinia-плагины концептуально похожи на плагины Vuex, но обладают рядом отличий:
Pinia делает плагины более декларативными и менее связанными с внутренней реализацией хранилища.
localStorage или
IndexedDB;Плагины Pinia формируют уровень расширяемости, который позволяет масштабировать архитектуру состояния без усложнения самих store.