# Модули Из-за использования единого дерева состояния, все глобальные данные приложения оказываются помещены в один большой объект. По мере роста приложения, хранилище может существенно раздуться. Чтобы помочь в этой беде, Vuex позволяет разделять хранилище на **модули**. Каждый модуль может содержать собственное состояние, мутации, действия, геттеры, и даже встроенные подмодули — структура фрактальна: ``` js const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> состояние модуля `moduleA` store.state.b // -> состояние модуля `moduleB` ``` ### Локальное состояние модулей Первым аргументом, который получают мутации и геттеры, будет **локальное состояние модуля**. ``` js const moduleA = { state: { count: 0 }, mutations: { increment (state) { // `state` указывает на локальное состояние модуля state.count++ } }, getters: { doubleCount (state) { return state.count * 2 } } } ``` Аналогично, `context.state` в действиях также указывает на локальное состояние модуля, а корневое — доступно в `context.rootState`: ``` js const moduleA = { // ... actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } } } ``` Кроме того, в геттеры корневое состояние передаётся 3-м параметром: ``` js const moduleA = { // ... getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } } } ``` ### Пространства имён По умолчанию действия, мутации и геттеры внутри модулей регистрируются в **глобальном пространстве имён** — это позволяет нескольким модулям реагировать на тот же тип мутаций/действий. Если вы хотите сделать модули более самодостаточными и готовыми для переиспользования, вы можете создать его с собственным пространством имён, указав опцию `namespaced: true`. Когда модуль будет зарегистрирован, все его геттеры, действия и мутации будут автоматически связаны с этим пространством имён, основываясь на пути по которому зарегистрирован модуль. Например: ``` js 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'] } } } } } }) ``` Геттеры и действия с собственным пространством имён будут получать свои локальные `getters`, `dispatch` и `commit`. Другими словами, вы можете использовать содержимое модуля без написания префиксов в том же модуле. Переключения между пространствами имён не влияет на код внутри модуля. #### Доступ к глобальному содержимому в модулях со своим пространством имён Если вы хотите использовать глобальное состояние и геттеры, `rootState` и `rootGetters` передаются 3-м и 4-м аргументами в функции геттеров, а также как свойства в объекте `context`, передаваемом в функции действий. Для запуска действий или совершения мутаций в глобальном пространстве имён нужно добавить `{ root: true }` 3-м аргументом в `dispatch` и `commit`. ``` js modules: { foo: { namespaced: true, getters: { // `getters` ограничены геттерами данного модуля // вы можете использовать rootGetters из 4-го аргумента геттеров someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter // -> 'foo/someOtherGetter' rootGetters.someOtherGetter // -> 'someOtherGetter' }, someOtherGetter: state => { ... } }, actions: { // dispatch и commit также ограничены данным модулем // они принимают опцию `root` для вызова в глобальном пространстве имён someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter // -> 'foo/someGetter' rootGetters.someGetter // -> 'someGetter' dispatch('someOtherAction') // -> 'foo/someOtherAction' dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction' commit('someMutation') // -> 'foo/someMutation' commit('someMutation', null, { root: true }) // -> 'someMutation' }, someOtherAction (ctx, payload) { ... } } } } ``` #### Подключение с помощью вспомогательных функций к пространству имён Подключение модуля со своим пространством имён к компонентам с помощью вспомогательных функций `mapState`, `mapGetters`, `mapActions` и `mapMutations` это может выглядеть подобным образом: ``` js computed: { ...mapState({ a: state => state.some.nested.module.a, b: state => state.some.nested.module.b }) }, methods: { ...mapActions([ 'some/nested/module/foo', 'some/nested/module/bar' ]) } ``` В таких случаях вы можете передать строку с пространством имён в качестве первого аргумента к вспомогательным функциям, тогда все привязки будут выполнены в контексте этого модуля. Пример выше можно упростить до: ``` js computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b }) }, methods: { ...mapActions('some/nested/module', [ 'foo', 'bar' ]) } ``` Кроме того, вы можете создать вспомогательные функции с помощью `createNamespacedHelpers`. Она возвращает объект, в котором все вспомогательные функции для связывания с компонентами будут указывать на переданное пространство имён: ``` js import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions } = createNamespacedHelpers('some/nested/module') export default { computed: { // будет указывать на `some/nested/module` ...mapState({ a: state => state.a, b: state => state.b }) }, methods: { // будет указывать на `some/nested/module` ...mapActions([ 'foo', 'bar' ]) } } ``` #### Уточнение для разработчиков плагинов Вас может обеспокоить непредсказуемость пространства имён для ваших модулей, когда вы создаёте [плагин](plugins.md) с собственными модулями и возможностью пользователям добавлять их в хранилище Vuex. Ваши модули будут также помещены в пространство имён, если пользователи плагина добавляют ваши модули в модуль со своим пространством имён. Чтобы приспособиться к этой ситуации, вам может потребоваться получить значение пространства имён через настройки плагина: ``` js // получение значения пространства имён через options // и возвращение функции плагина Vuex export function createPlugin (options = {}) { return function (store) { // добавление пространства имён к модулям плагина const namespace = options.namespace || '' store.dispatch(namespace + 'pluginAction') } } ``` ### Динамическая регистрация модулей Вы можете зарегистрировать модуль уже и **после** того, как хранилище было создано, используя метод `store.registerModule`: ``` js // регистрация модуля `myModule` store.registerModule('myModule', { // ... }) // регистрация вложенного модуля `nested/myModule` store.registerModule(['nested', 'myModule'], { // ... }) ``` Состояние модуля будет доступно как `store.state.myModule` и `store.state.nested.myModule`. Динамическая регистрация модулей позволяет другим плагинам Vue также использовать Vuex для управления своим состоянием, добавляя модуль к хранилищу данных приложения. Например, библиотека [`vuex-router-sync`](https://github.com/vuejs/vuex-router-sync) интегрирует vue-router во vuex, отражая изменение текущего пути приложения в динамически присоединённом модуле. Удалить динамически зарегистрированный модуль можно с помощью `store.unregisterModule(moduleName)`. Обратите внимание, что статические (определённые на момент создания хранилища) модули при помощи этого метода удалить не получится. ### Повторное использование модулей Иногда нам может потребоваться создать несколько экземпляров модуля, например: - Создание нескольких хранилищ, которые используются одним модулем (например, чтобы [избегать синглтонов с сохранением состояния в SSR](https://ssr.vuejs.org/ru/structure.html#избегайте-синглтонов-с-состоянием) при использовании опции `runInNewContext` в значении `false` или `'once'`); - Регистрация модуля несколько раз в одном хранилище. Если мы используем просто объект для определения состояния модуля, тогда этот объект состояния будет использоваться по ссылке и вызывать загрязнение состояния хранилища / модуля при его мутациях. Это фактически та же самая проблема с `data` внутри компонентов Vue. Таким образом решение будет таким же - использовать функцию для объявления состояния модуля (поддержка добавлена в версии 2.3.0+): ``` js const MyReusableModule = { state () { return { foo: 'bar' } }, // мутации, действия, геттеры... } ```