Плагины для Pinia

Pinia предоставляет мощный механизм расширения функциональности через плагины, что позволяет добавлять общие функции, логирование, обработку ошибок и асинхронные операции во все хранилища приложения. Плагин — это функция, принимающая два аргумента: context и store.

Сигнатура плагина:

function myPlugin(context) {
  return (store) => {
    // Расширение функционала хранилища
  }
}
  • context содержит глобальные возможности Pinia, включая метод pinia, доступ к другим хранилищам и опционально app.
  • store — конкретное хранилище, которое подключается к плагину.

Плагин подключается через метод use при создании Pinia:

import { createPinia } from 'pinia';

const pinia = createPinia();
pinia.use(myPlugin);

Добавление свойств и методов через плагины

Один из распространённых сценариев — добавление новых свойств или методов в каждый store. Это позволяет унифицировать логику, например, реализовать единое логирование изменений состояния.

function loggerPlugin() {
  return (store) => {
    store.$log = (msg) => {
      console.log(`[${store.$id}] ${msg}`, store.$state);
    };
  };
}

pinia.use(loggerPlugin);

После подключения плагина любой store получает метод $log:

const useUserStore = defineStore('user', {
  state: () => ({ name: 'Alice' }),
});

const userStore = useUserStore();
userStore.$log('Store инициализирован');

Доступ к другим хранилищам внутри плагина

Pinia позволяет обращаться к другим store внутри плагина через объект pinia в контексте:

function crossStorePlugin(context) {
  return (store) => {
    const settingsStore = context.pinia._s.get('settings');
    if (settingsStore) {
      store.$settings = settingsStore;
    }
  };
}

Это особенно полезно при создании глобальных функций, зависящих от нескольких хранилищ, например, проверки авторизации на основе настроек приложения.

Логирование изменений состояния

Pinia поддерживает реактивное слежение за состоянием store через подписки ($subscribe). Плагин может автоматически подключать логирование всех изменений:

function stateLoggerPlugin() {
  return (store) => {
    store.$subscribe((mutation, state) => {
      console.log(`[Mutation] ${mutation.storeId}:`, mutation.events, state);
    });
  };
}

pinia.use(stateLoggerPlugin);

Такое решение позволяет централизованно отслеживать все изменения, не дублируя код в каждом store.

Плагины для асинхронной логики и API

Плагины удобно использовать для интеграции с внешними API. Например, можно автоматически добавлять методы для загрузки данных:

function apiPlugin(context) {
  return (store) => {
    store.$fetchData = async (url) => {
      try {
        const response = await fetch(url);
        const data = await response.json();
        store.$state = { ...store.$state, ...data };
      } catch (error) {
        console.error(`[${store.$id}] Ошибка при загрузке:`, error);
      }
    };
  };
}

В дальнейшем любой store сможет использовать $fetchData без дублирования логики.

Параметризованные плагины

Плагины могут принимать параметры при подключении, что позволяет гибко настраивать их поведение:

function configurableLogger(options = { level: 'info' }) {
  return (store) => {
    store.$log = (msg) => {
      if (options.level === 'debug') {
        console.debug(`[${store.$id}] ${msg}`);
      } else {
        console.log(`[${store.$id}] ${msg}`);
      }
    };
  };
}

pinia.use(configurableLogger({ level: 'debug' }));

Типизация и TypeScript

При использовании TypeScript можно расширять типы store для корректной поддержки новых методов и свойств. Например:

import { PiniaPluginContext } from 'pinia';

function typedPlugin(context: PiniaPluginContext) {
  return (store: any) => {
    store.$log = (msg: string) => console.log(`[${store.$id}] ${msg}`);
  };
}

declare module 'pinia' {
  export interface PiniaCustomProperties {
    $log(msg: string): void;
  }
}

Это обеспечивает автодополнение и строгую проверку типов для всех новых функций, добавленных через плагины.

Практические сценарии использования

  • Глобальное логирование действий пользователей. Плагин отслеживает изменения состояния и отправляет их в аналитическую систему.
  • Кэширование данных. Плагин автоматически сохраняет состояние store в localStorage или IndexedDB.
  • Интеграция с API. Методы для асинхронного запроса данных становятся доступными во всех store.
  • Межхранилищная коммуникация. Плагин обеспечивает доступ к другим store без прямого импорта.

Рекомендации по проектированию плагинов

  • Стараться делать плагины универсальными и независимыми от конкретного store.
  • Не модифицировать state напрямую без использования реактивных методов Pinia.
  • Обеспечивать возможность отключения или параметризации плагина.
  • Использовать $subscribe и реактивные свойства для слежения за состоянием вместо прямых мутаций.

Плагины в Pinia позволяют создавать мощные, централизованные решения для повторного использования логики, упрощают поддержку и делают архитектуру приложения более масштабируемой.