Vuex
Руководство по использованию Vuex.
Когда использовать?
Vuex используем, когда приложение требует особого контроля и мониторинга данных. В иных случаях будет достаточно самописных db.js
файлов или глобальных прототипов Vue.
Разделение бизнес-логики и интерфейса
Vuex запрещено использовать для управления элементами интерфейса (например, добавлять флаги и индикаторы для модальных окон, пагинации, кнопок и т.п.)
Vuex используется только для хранения бизнес-логики приложения: пользователи, настройки, ключи и т.п...
Модульный подход
В зависимости от типа проекта, мы будем использовать или НЕ использовать модульный подход Vuex.
В малых и средних проектах его использование можно избегать, но в крупных проектах его использование обязательно.
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// содержимое модуля
state: () => ({ ... }), // состояние модуля автоматически вложено и не зависит от опции пространства имён
getters: {
isAdmin () { ... }, // -> getters['account/isAdmin']
},
actions: {
login () { ... }, // -> dispatch('account/login')
},
mutations: {
login () { ... }, // -> commit('account/login')
},
// вложенные модули
modules: {
// наследует пространство имён из родительского модуля
myPage: {
state: () => ({ ... }),
getters: {
profile () { ... }, // -> getters['account/profile']
},
},
// большая вложенность с собственным пространством имён
posts: {
namespaced: true,
state: () => ({ ... }),
getters: {
popular () { ... }, // -> getters['account/posts/popular']
},
}
}
}
}
});
Структура папок при модульном подходе
store
modules
user
actions.js
getters.js
mutations.js
index.js
state.js
actions.js
getters.js
mutations.js
index.js
state.js
Причём если у модуля есть свои модули, то вложенность идёт дальше по модулям в соответствии с корнем (как в примере сверху с постами пользователя).
import Vue from 'vue';
import Vuex from 'vuex';
import state from './state.js';
import actions from './actions.js';
import mutations from './mutations.js';
import getters from './getters.js';
import user from './modules/user';
Vue.use(Vuex);
export default new Vuex.Store({
state,
mutations,
actions,
getters,
modules: {
user,
},
});
state
Если мы работаем с API и храним полученные от него данные, то они должны оставаться в том же виде, в котором мы их получили с сервера - неизменными. Если какие-либо данные нам нужны в отсортированном виде (либо любом другом), то добавляем в getters
новое значение.
Данные из state могут использоваться для чтения в любом месте Vue приложения.
Старайтесь избегать мутаций свойств объектов из state. Нам нужно наиболее предсказуемое поведение приложений.
const store = new Vuex.Store({
state: () => ({
// обязательно snake_case
user: {...},
csrf: "...",
}),
});
getters
Геттеры мы используем только для получения данных из state, а также для создания фильтров для данных.
const store = new Vuex.Store({
getters: {
// обязательно camelCase
someGetter (state) {
return state.user;
},
// getter с доп. фильтром извне
someFilteredGetter: (state) => (is_active) => {
return state.users.filter((user) => user.is_active === is_active );
},
},
});
mutations
const store = new Vuex.Store({
mutations: {
// обязательно UPPERCASE + snake_case
SET_POSTS: (state, posts) => {
state.posts = posts;
},
},
});
Вторым параметром мы никогда не передаём название как payload. Мы должны сразу понимать что за сущность мы принимаем в мутацию. Мы также не используем Object-Style Commit.
Использование Mutation Types
Часто встречается шаблон использования констант для типов мутаций в различных реализациях Flux. Это позволяет коду использовать такие инструменты, как линтеры, а размещение всех констант в одном файле позволяет вашим соавторам с первого взгляда получить представление о том, какие мутации возможны во всем приложении:
// mutation-types.js
export const SET_POSTS = 'SET_POSTS';
export const SOME_MUTATION = 'SOME_MUTATION';
// actions.js
import * as types from './mutation_types';
commit(types.SET_POSTS, posts);
commit(types.SOME_MUTATION, some_data);
// mutations.js
import * as types from './mutation_types';
export default {
[types.SET_POSTS](state, posts) {
state.posts = posts;
},
[types.SOME_MUTATION](state, some_data) {
state.some_data = some_data;
},
};
Это особенно полезно в крупных проектах. В малых и средних проектах это не обязательно.
Мутации должны быть синхронными
Следует помнить одно важное правило: функции обработчика мутаций должны быть синхронными. Почему? Рассмотрим следующий пример:
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
Теперь представьте, что мы отлаживаем приложение и просматриваем журналы изменений devtool. Для каждой зарегистрированной мутации инструменту разработки необходимо будет делать снимки состояния «до» и «после». Однако асинхронный обратный вызов внутри приведенного выше примера мутации делает это невозможным: обратный вызов еще не вызывается, когда мутация зафиксирована, и инструмент разработки не может узнать, когда на самом деле будет вызван обратный вызов — любая мутация состояния, выполненная в обратном вызове. по существу не отслеживается!
actions
Когда мы используем какой-либо action внутри компонентов, все необходимые мутации должны происходить автоматически. Также мы должны иметь доступ к запрашиваемым данным.
При использовании Promise()
, всегда прокидывайте также reject(err)
// плохо
actions: {
fetchPosts() {
return new Promise((resolve, reject) => {
api.get_posts()
.then(({ data }) => resolve(data.posts))
.catch((err) => reject(err))
});
}
}
this.$store.dispatch('fetchPosts')
.then((posts) => {
this.$store.commit('SET_POSTS', posts);
});
// хорошо
actions: {
fetchPosts({ commit }) {
return new Promise((resolve, reject) => {
api.get_posts()
.then(({ data }) => {
commit('SET_POSTS', data.posts);
resolve(data.posts);
})
.catch((err) => reject(err))
});
}
}
this.$store.dispatch('fetchPosts');
helper-функции
Функции mapGetters
, mapActions
, mapMutations
, mapState
- запрещено использовать.
Вместо этого, используйте варианты:
this.$store.state.some_state
this.$store.getters['someGetter']
this.$store.dispatch('someAction')
this.$store.commit('SOME_MUTATION', args)
Last updated