В основе масштабного приложения на Vue.js лежит хранилище, в котором находятся все данные. Хранилище Vuex в приложении на Vue.js служит единым источником истины, который обеспечивает отличную производительность и реактивность из коробки. По мере того, как обрастает кодом ваше приложение, количество данных в хранилище Vuex также растет, и управлять ими становится труднее. Проектирование управления состоянием вашего приложения на основе лучших практик поможет решить большинство проблем, которые появляются по мере усложнения приложения.
В этой статье мы поговорим о некоторых лучших практиках и советах по проектированию архитектуры механизма управления состоянием в масштабном приложении на Vue.js. Мы поговорим о следующих пяти концепциях, которые помогут спроектировать хранилище наилучшим образом.
- Структурирование хранилища
- Разделение хранилища на модули
- Автоматический импорт модулей хранилища
- Сброс состояния модуля
- Глобальный сброс состояния модуля
Структурирование хранилища
Хранилище Vuex состоит из четырех компонентов:
- Объекта состояния
- Функции getter
- Действий
- Мутаций
Если вы еще не знакомы с этими четырьмя концепциями, то сейчас мы вкратце о них поговорим. Объект состояния хранит данные вашего приложения в виде большого JSON-файла. Функция getter помогает вам получить доступ к этим объектам состояния извне, она может вести себя как реактивное вычисляемое свойство. Мутации, как следует из названия, используются для мутации/изменения объекта состояния. Действия очень похожи на мутации, однако, вместо того чтобы изменять состояние, действие коммитят мутации. Действия могут содержать любой асинхронный код или бизнес-логику.
Vuex рекомендует, чтобы объект состояния изменялся только внутри функции-мутации. Также желательно не запускать какой-либо тяжелый или блокирующий код внутри функции-мутации, так как по своей природе он синхронный. Вместо этого нужно использовать действия, которые должны быть спроектированы асинхронно для выполнения тяжелых работ или выполнения сетевых запросов и коммитов мутаций. Действия являются лучшим местом для хранения бизнес-логики и логики обработки данных. Действия считаются лучшими кандидатами на эту роль, потому что вы можете их использовать для отправки данных в хранилище или для отправки данных непосредственно в компоненты Vue.
Хорошей практикой является отказ от прямого доступа к объекту состояния и использование вместо него getter’а. Функцию getter можно легко смаппить в любой компонент Vue с помощью mapGetter, в качестве вычисляемых свойств.
Разделение хранилища на модули
Неудивительно, что с увеличением размера и сложности хранилище захламляемся и становится более трудным к пониманию. Vuex из коробки предоставляет возможность разделить хранилище на отдельные модули согласно целям в вашем приложении. Разделение бизнес-логики с помощью модульного хранилища повышает удобство сопровождения приложения. Поэтому нам нужно убедиться, что каждый модуль вынесен в отдельное пространство имен, а не обращаться к ним с помощью глобального контекста хранения.
Вот небольшой пример создания модуля хранилища и того, как объединить все модули в основном хранилище.
Структура каталога
store/ ├── index.js ---> Main Store file └── modules/ ├── module1.store.js ├── module2.store.js ├── module3.store.js ├── module4.store.js ├── module5.store.js └── module6.store.js
Обратите внимание, что каждый модуль назван по принципу ModuleName.store.js, что поможет в будущем при автоматическом импорте этих модулей, но об этом мы поговорим в следующем разделе.
Авторинг модулей
Мы можем переместить сетевые вызовы в отдельные JavaScript-файлы, но об этом мы поговорим в другой статье, которая будет посвящена архитектуре сетевого уровня приложения. Мы даже можем разделить объекты состояния, getter’ы, действия и мутации на разные файлы для удобства чтения. Хорошо хранить все связанные функций в одном месте и разбивать хранилище на модули все дальше и дальше, если оно все еще слишком большое и сложное.
/* Module1.store.js */ // State object const state = { variable1: value, variable2: value, variable3: value } // Getter functions const getters = { getVariable1( state ) { return state.variable1; }, getVariable2( state ) { return state.variable2; }, .... } // Actions const actions = { fetchVariable1({ commit }) { return new Promise( (resolve, reject) => { // Make network request and fetch data // and commit the data commit('SET_VARIABLE_1', data); resolve(); } }, .... } // Mutations const mutations = { SET_VARIABLE_1(state, data) { state.variable1 = data; }, SET_VARIABLE_2(state, data) { state.variable2 = data; }, .... } export default { namespaced: true, state, getters, actions, mutations }
Комбинирование модулей
/** store/index.js **/ import Vue from 'vue'; import Vuex from 'vuex'; import createLogger from 'vuex/dist/logger'; import Module1 from './modules/module1.store'; import Module2 from './modules/module2.store'; ... Vue.use(Vuex); const debug = process.env.NODE_ENV !== 'production'; export default new Vuex.Store({ modules: { Module1, Module2, ... }, strict: debug, plugins: debug? [ createLogger() ] : [], }
Автоматический импорт модулей хранилища
Как я уже говорил, если модули становятся все сложнее и больше, их нужно разделить на подмодули для уменьшения сложности. Когда количество модулей увеличивается, управлять этими модулями по отдельности становится действительно сложно, а в особенности импортировать каждый из них. У нас будет небольшой JS-файл в каталоге modules, который сделает эту работу за нас. Он поможет нам объединить вместе все модули.
Чтобы он заработал, рекомендуется следовать строгой структуре именования файлов модулей. В конечном итоге, наличие стандарта именования повысит удобство поддержки всего проекта. Для упрощения работы модули можно назвать в нотации camelCase, и добавить расширение .store.js. Например, для файла userData.store.js нам нужно будет добавить файл index.js внутрь подкаталога модулей, чтобы найти все нужные модули и экспортировать их в основное хранилище.
store/ ├── index.js ---> Main Store file └── modules/ ├── index.js --> Auto exporter ├── module1.store.js └── module2.store.js
Скрипт для автоматического экспорта
/** * Automatically imports all the modules and exports as a single module object */ const requireModule = require.context('.', false, /\.store\.js$/); const modules = {}; requireModule.keys().forEach(filename => { // create the module name from fileName // remove the store.js extension and capitalize const moduleName = filename .replace(/(\.\/|\.store\.js)/g, '') .replace(/^\w/, c => c.toUpperCase()) modules[moduleName] = requireModule(filename).default || requireModule(filename); }); export default modules;
Теперь, когда наш скрипт для автоматического экспорта находится там, где нужно, мы можем начать импорт в основное хранилище и получить доступ ко всем модулям.
import Vue from 'vue' import Vuex from 'vuex' import createLogger from 'vuex/dist/logger' // import the auto exporter import modules from './modules'; Vue.use(Vuex); const debug = process.env.NODE_ENV !== 'production'; export default new Vuex.Store({ modules, // all your modules automatically imported :) strict: debug, plugins: debug ? [createLogger()] : [] // set logger only for development })
Как только вы использовали автоматический импорт в основном хранилище, все новые модули, добавляемые в подкаталог modules будут автоматически импортированы. Например, если у вас есть файл с именем user.store.js, он будет импортирован как модуль хранения в пространстве имен User. Вы можете использовать это пространство имен для маппинга getter’ов и действий в компоненты с помощью mapGetters и mapActions.
Сброс состояния модуля
Если вы когда-нибудь работали с приложениями на Vue + Vuex, которые обрабатывают большое количество данных, то, возможно, сталкивались со сценарием, когда нужно было сбросить состояние хранилища. Функция сброса довольно распространенная механика. В случае, когда в вашем приложении есть аутентификация пользователя, вы можете сбросить хранилище при выходе пользователя из приложения.
Чтобы сбросить хранилище, нужно перевести объект состояния в исходное состояние и скопировать его основное состояние. Для этого можно просто использовать функцию, которая возвращает начальное состояние. В модуле хранилища создайте функцию initialState(), пусть она возвращает текущий объект состояния.
const initialState = () => ({ variable1: value, variable2: value, variable3: value }); const state = initialState();
Теперь у нас есть отдельное изначальное состояние, и любые изменения, которые мы произведем с состоянием, не повлияют на фактическое изначальное состояние. Таким образом, мы можем использовать полученную функцию для сброса хранилища. Создайте функцию-мутацию, которая с помощью изначального состояния изменит весь объект хранилища.
const initialState = () => ({ variable1: value, variable2: value, variable3: value }); const state = initialState(); // Getters // Actions // Mutations const mutations = { RESET(state) { const newState = initialState(); Object.keys(newState).forEach(key => { state[key] = newState[key] }); }, // other mutations }
Как только у нас получилась мутация RESET, мы можем использовать ее для сброса хранилища вызвав действие, либо закоммитив мутацию RESET.
// Actions const actions = { reset({ commit }) { commit('RESET'); }, }
Глобальный сброс состояния модуля
Что делать, если нам нужно сбросить хранилище целиком? Вместе со всеми модулями? Если вы выполнили 4-й и 5-й пункт настройки автоматического импорта и мутации сброса состояния модуля для всех ваших модулей, мы можем использовать следующее действие в файле основного хранилища, чтобы сбросить все модули одновременно.
import Vue from 'vue' import Vuex from 'vuex' import createLogger from 'vuex/dist/logger' import modules from './modules'; Vue.use(Vuex); const debug = process.env.NODE_ENV !== 'production'; export default new Vuex.Store({ modules, actions: { reset({commit}) { // resets state of all the modules Object.keys(modules).forEach(moduleName => { commit(`${moduleName}/RESET`); }) } }, strict: debug, plugins: debug ? [createLogger()] : [] // set logger only for development });
Обратите внимание, что действие, которое мы создали, находится в основном файле хранилища, а не внутри какого-то конкретного модуля. Это действие можно запустить из любого места вашего компонента Vue с помощью следующей строки кода:
this.$store.dispatch('reset');
Что дальше?
Вам понравилась статья? В следующих статьях мы более подробно обсудим аспекты сетевой архитектуры нашего приложения на Vue.js. Мы поговорим о методах, которые используются для управления учетными данными при аутентификации, перехватчиках и обработке ошибок в сетевых запросах.
Чтобы узнать больше о том, чем мы занимаемся в Locale.ai, прочтите здесь о неизведанных землях геопространственной аналитики.
Особую благодарность мы выражаем Крису Фритсу за его удивительный рассказ о 7 вещах, которые скрывают от вас консультанты Vue. Он подкинул нам несколько идей, которые мы использовали в этой статье.
ссылка на оригинал статьи https://habr.com/ru/articles/506754/
Добавить комментарий