State, getters и actions

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


State

State — это объект, который хранит все данные приложения. Он является единственным источником правды. Все компоненты получают доступ к state через store, что позволяет избегать дублирования данных и рассинхронизации.

Пример определения state:

// store/index.js
export const state = () => ({
  user: null,
  isAuthenticated: false,
  cart: []
});

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

  • State должен быть реактивным, чтобы изменения автоматически отражались во всех компонентах.
  • Никогда не изменять state напрямую вне мутаций — это нарушает принципы Vuex и делает состояние непредсказуемым.

Доступ к state из компонентов осуществляется через:

computed: {
  currentUser() {
    return this.$store.state.user;
  }
}

Getters

Getters — это вычисляемые свойства для state. Они позволяют получать данные из store с предварительной обработкой, не изменяя само состояние.

Пример использования getters:

export const getters = {
  isLoggedIn: (state) => {
    return state.isAuthenticated;
  },
  cartItemCount: (state) => {
    return state.cart.length;
  }
};

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

  • Getters можно использовать как функции с параметрами, возвращая функцию:
export const getters = {
  getCartItemById: (state) => (id) => {
    return state.cart.find(item => item.id === id);
  }
};
  • Getters реактивны: при изменении state автоматически обновляются все компоненты, которые их используют.
  • В компонентах доступ через:
computed: {
  loggedIn() {
    return this.$store.getters.isLoggedIn;
  }
}

Mutations

Mutations — единственный способ изменять state. Они синхронны и принимают два параметра: state и payload.

Пример:

export const mutations = {
  setUser(state, user) {
    state.user = user;
    state.isAuthenticated = !!user;
  },
  addToCart(state, product) {
    state.cart.push(product);
  },
  removeFromCart(state, productId) {
    state.cart = state.cart.filter(item => item.id !== productId);
  }
};

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

  • Все мутации должны быть синхронными.
  • Любые изменения state вне мутаций считаются антипаттерном.

В компоненте мутации вызываются через:

this.$store.commit('setUser', user);
this.$store.commit('addToCart', product);

Actions

Actions — функции для выполнения асинхронных операций, таких как HTTP-запросы, и последующего вызова мутаций.

Пример:

export const actions = {
  async fetchUser({ commit }) {
    const response = await fetch('/api/user');
    const data = await response.json();
    commit('setUser', data);
  },
  async addProductToCart({ commit }, product) {
    // имитация задержки сервера
    await new Promise(resolve => setTimeout(resolve, 500));
    commit('addToCart', product);
  }
};

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

  • Actions могут быть асинхронными, но мутации остаются синхронными.
  • Actions вызываются через:
this.$store.dispatch('fetchUser');
  • Actions могут вызывать другие actions и использовать getters и state:
export const actions = {
  async checkout({ state, commit, getters }) {
    if (getters.cartItemCount === 0) return;
    await api.checkout(state.cart);
    commit('clearCart');
  }
};

Организация модулей

Quasar позволяет структурировать store с помощью модулей, чтобы разделять логику по функциональным блокам:

// store/user.js
export const state = () => ({
  profile: null
});

export const mutations = {
  setProfile(state, profile) {
    state.profile = profile;
  }
};

export const actions = {
  async fetchProfile({ commit }) {
    const data = await fetch('/api/profile').then(res => res.json());
    commit('setProfile', data);
  }
};

export const getters = {
  userName: (state) => state.profile?.name || ''
};

Модули можно подключать с namespaced: true для изоляции пространства имен:

export const namespaced = true;

Доступ к таким модулям:

this.$store.dispatch('user/fetchProfile');
this.$store.getters['user/userName'];

Рекомендации по использованию

  • Всегда разделять state, getters, mutations и actions для ясности.
  • Асинхронные операции должны выполняться только в actions.
  • Использовать modules с namespaced: true для крупных проектов.
  • Getters использовать для вычисления производных данных, а не для прямого изменения state.
  • Избегать прямых изменений state в компонентах.

Эта структура позволяет создавать масштабируемые, поддерживаемые приложения с Quasar и Vuex, обеспечивая централизованное управление состоянием, предсказуемость и реактивность компонентов.