Модули
Из-за использования единого дерева состояния все состояния нашего приложения содержатся внутри одного большого объекта. Однако, по мере роста нашего приложения, хранилище может сильно раздуться.
Для решения этой проблемы Vuex позволяет нам разделить наше хранилище на модули. Каждый модуль может содержать собственное состояние, мутации, действия, геттеры и даже вложенные модули — это фрактальная структура:
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`'s state
store.state.b // -> `moduleB`'s state
Состояние локального модуля
В мутациях и геттерах модуля первым аргументом будет локальное состояние модуля.
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// `state` is the local module state
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
Аналогично, внутри действий модуля context.state будет доступно локальное состояние, а корневое состояние будет доступно как context.rootState.
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
Также, внутри геттеров модуля корневое состояние будет доступно в качестве их третьего аргумента:
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
Имена пространств
По умолчанию, действия и мутации всё ещё регистрируются в глобальном пространстве имён — это позволяет нескольким модулям реагировать на один и тот же тип действия/мутации. Геттеры также по умолчанию регистрируются в глобальном пространстве имён. Однако, в настоящее время это не имеет функционального значения (так как есть, чтобы избежать разрыва совместимости). Следует быть осторожным, чтобы не определить два геттера с одинаковым именем в разных, не имеющих пространств имён, модулях, что приведёт к ошибке.
Если вы хотите, чтобы ваши модули были более самостоятельными или переиспользуемыми, вы можете пометить их как имеющие пространство имён с namespaced: true. При регистрации модуля все его геттеры, действия и мутации будут автоматически иметь пространство имён, основанное на пути регистрации модуля. Например:
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// module assets
state: () => ({ ... }), // module state is already nested and not affected by namespace option
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// nested modules
modules: {
// inherits the namespace from parent module
myPage: {
state: () => ({ ... }),
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// further nest the namespace
posts: {
namespaced: true,
state: () => ({ ... }),
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
Геттеры и действия с именами пространств будут получать локализованные getters, dispatch и commit. Другими словами, вы можете использовать ресурсы модуля без написания префикса в том же самом модуле. Переключение между именованием пространств и его отсутствием не влияет на код внутри модуля.
Доступ к глобальным ресурсам в модулях с именами пространств
Если вы хотите использовать глобальное состояние и геттеры, rootState и rootGetters передаются в качестве третьего и четвёртого аргументов функциям геттера, а также доступны как свойства объекта context, передаваемого функциям действия.
Для отправки действий или применения мутаций в глобальном пространстве имён, передайте { root: true } в качестве третьего аргумента к dispatch и commit.
modules: {
foo: {
namespaced: true,
getters: {
// `getters` is localized to this module's getters
// you can use rootGetters via 4th argument of getters
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
rootGetters['bar/someOtherGetter'] // -> 'bar/someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// dispatch and commit are also localized for this module
// they will accept `root` option for the root dispatch/commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
rootGetters['bar/someGetter'] // -> 'bar/someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
Регистрация глобального действия в модулях с именами пространств
Если вы хотите зарегистрировать глобальные действия в модулях с именами пространств, вы можете пометить их root: true и поместить определение действия в функцию handler. Например:
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction')
}
},
modules: {
foo: {
namespaced: true,
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}
Связывание вспомогательных функций с именем пространства
При связывании модуля с именованным пространством с помощью mapState, mapGetters, mapActions и mapMutations вспомогательных функций, это может быть немного громоздко:
computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
}),
...mapGetters([
'some/nested/module/someGetter', // -> this['some/nested/module/someGetter']
'some/nested/module/someOtherGetter', // -> this['some/nested/module/someOtherGetter']
])
},
methods: {
...mapActions([
'some/nested/module/foo', // -> this['some/nested/module/foo']()
'some/nested/module/bar' // -> this['some/nested/module/bar']()
])
}
В таких случаях вы можете передать строку имени пространства модуля в качестве первого аргумента вспомогательных функций, чтобы все привязки выполнялись с использованием этого модуля в качестве контекста. Вышеприведённый код можно упростить до:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
}),
...mapGetters('some/nested/module', [
'someGetter', // -> this.someGetter
'someOtherGetter', // -> this.someOtherGetter
])
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}
Кроме того, вы можете создавать вспомогательные функции с именами пространств, используя createNamespacedHelpers. Оно возвращает объект с новыми вспомогательными функциями привязки компонентов, которые связаны со значением указанного имени пространства:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// look up in `some/nested/module`
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// look up in `some/nested/module`
...mapActions([
'foo',
'bar'
])
}
}
Предостережение для разработчиков плагинов
Вам может потребоваться позаботиться о непредсказуемом именовании пространств для ваших модулей, когда вы создаёте плагин, который предоставляет модули и позволяет пользователям добавлять их в хранилище Vuex. Ваши модули также будут иметь пространство имён, если пользователи плагина добавляют ваши модули в модуль с именованным пространством. Чтобы адаптироваться к этой ситуации, вам может потребоваться получить значение имени пространства через опцию вашего плагина:
// get namespace value via plugin option
// and returns Vuex plugin function
export function createPlugin (options = {}) {
return function (store) {
// add namespace to plugin module's types
const namespace = options.namespace || ''
store.dispatch(namespace + 'pluginAction')
}
}
Динамическая регистрация модулей
Вы можете зарегистрировать модуль после создания хранилища с помощью метода store.registerModule.
import Vuex from 'vuex'
const store = new Vuex.Store({ /* options */ })
// register a module `myModule`
store.registerModule('myModule', {
// ...
})
// register a nested module `nested/myModule`
store.registerModule(['nested', 'myModule'], {
// ...
})
Состояние модуля будет доступно как store.state.myModule и store.state.nested.myModule.
Динамическая регистрация модулей позволяет другим плагинам Vue использовать Vuex для управления состоянием путём добавления модуля в хранилище приложения. Например, библиотека vuex-router-sync (opens new window) интегрирует vue-router с vuex, управляя состоянием маршрута приложения в динамически подключённом модуле.
Вы также можете удалить динамически зарегистрированный модуль с помощью store.unregisterModule(moduleName). Обратите внимание, что статические модули (объявленные при создании хранилища) нельзя удалить с помощью этого метода.
Обратите внимание, что вы можете проверить, зарегистрирован ли модуль в хранилище или нет, с помощью метода store.hasModule(moduleName).
Сохранение состояния
Вероятно, вам потребуется сохранить предыдущее состояние при регистрации нового модуля, например, сохранение состояния из приложения с предварительной рендерингом на сервере. Этого можно добиться с помощью опции preserveState: store.registerModule('a', module, { preserveState: true }).
Когда вы установите preserveState: true, модуль регистрируется, действия, мутации и геттеры добавляются в хранилище, но состояние не добавляется. Предполагается, что состояние вашего хранилища уже содержит состояние для данного модуля и вы не хотите его перезаписывать.
Переиспользование модулей
Иногда нам может потребоваться создать несколько экземпляров модуля, например:
- Создание нескольких хранилищ, использующих один и тот же модуль (например, для избегания состояний-синглтонов в SSR (opens new window) при
runInNewContextопцииfalseили'once'); - Регистрация одного и того же модуля несколько раз в одном хранилище.
Если мы используем простой объект для объявления состояния модуля, то этот объект состояния будет разделяться по ссылке и вызовет загрязнение состояния между хранилищами/модулями при его изменении.
Это фактически та же самая проблема, что и с data внутри компонентов Vue. Поэтому решение также одинаковое — используйте функцию для объявления состояния модуля (поддерживается в 2.3.0+):
const MyReusableModule = {
state: () => ({
foo: 'bar'
}),
// mutations, actions, getters...
}
© 2015–present Evan You
Licensed under the MIT License.
https://vuex.vuejs.org/guide/modules.html