Композиция stores

В Quasar концепция store строится на базе Pinia, что обеспечивает реактивное управление состоянием приложения. Композиция stores позволяет создавать модульную структуру состояния, делая код более масштабируемым и поддерживаемым.


Создание базового store

Базовый store создаётся с помощью функции defineStore. В ней определяются:

  • id — уникальный идентификатор хранилища;
  • state — реактивные данные;
  • getters — вычисляемые свойства;
  • actions — методы для изменения состояния.

Пример:

import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    email: '',
    isLoggedIn: false
  }),
  getters: {
    userName: (state) => state.name.toUpperCase()
  },
  actions: {
    login(name, email) {
      this.name = name;
      this.email = email;
      this.isLoggedIn = true;
    },
    logout() {
      this.name = '';
      this.email = '';
      this.isLoggedIn = false;
    }
  }
});

Ключевое преимущество — реактивность: изменения state мгновенно отражаются в компонентах, использующих store.


Композиция нескольких stores

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

Пример композиции:

import { defineStore } from 'pinia';
import { useUserStore } from './userStore';

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: []
  }),
  getters: {
    itemCount: (state) => state.items.length
  },
  actions: {
    addItem(item) {
      const userStore = useUserStore();
      if (!userStore.isLoggedIn) {
        throw new Error('Пользователь должен войти в систему');
      }
      this.items.push(item);
    }
  }
});

Особенности:

  • Импорт другого store внутри actions обеспечивает доступ к состоянию и методам другого модуля.
  • Можно создавать цепочки действий между stores, поддерживая реактивность.

Использование Composition API с store

Quasar полностью совместим с Composition API Vue 3. Это позволяет подключать store в компоненты с использованием реактивных переменных и computed:

import { useUserStore } from 'src/stores/userStore';
import { computed } from 'vue';

export default {
  setup() {
    const userStore = useUserStore();

    const displayName = computed(() => userStore.userName);

    function loginUser() {
      userStore.login('Иван', 'ivan@example.com');
    }

    return { displayName, loginUser };
  }
}

Преимущество такого подхода — полная типизация, лаконичность кода и легкость тестирования.


Реактивная интеграция между stores

Pinia обеспечивает реактивность не только внутри одного store, но и между ними. Это позволяет строить сложные взаимозависимые состояния:

import { defineStore } from 'pinia';
import { useCartStore } from './cartStore';

export const useOrderStore = defineStore('order', {
  state: () => ({
    orders: []
  }),
  actions: {
    placeOrder() {
      const cartStore = useCartStore();
      if (cartStore.items.length === 0) return;

      this.orders.push({ items: [...cartStore.items], date: new Date() });
      cartStore.items = [];
    }
  }
});

Ключевые моменты:

  • Любое изменение в cartStore.items автоматически отслеживается в компонентах, использующих cartStore.
  • В orderStore можно безопасно считывать текущее состояние корзины и создавать новые сущности.

Составление store через функции и хуки

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

import { ref } from 'vue';

export function useNotifications() {
  const messages = ref([]);

  function addMessage(msg) {
    messages.value.push(msg);
  }

  function clearMessages() {
    messages.value = [];
  }

  return { messages, addMessage, clearMessages };
}

Применение в store:

import { defineStore } from 'pinia';
import { useNotifications } from './notifications';

export const useAlertStore = defineStore('alert', () => {
  const { messages, addMessage, clearMessages } = useNotifications();

  return { messages, addMessage, clearMessages };
});

Такой подход обеспечивает максимальную модульность и упрощает тестирование отдельных частей состояния.


Советы по композиции stores

  • Разделять состояние на логические модули, избегая гигантских stores.
  • Использовать actions для взаимодействия между модулями, не модифицируя состояние напрямую извне.
  • Применять Composition API для реактивных вычислений и уменьшения связности кода.
  • Создавать повторно используемые функции и хуки для общих операций состояния.

Композиция stores в Quasar обеспечивает масштабируемость, реактивность и чистую архитектуру приложения, делая код гибким для расширения и поддержки в будущем.