Releases: vuejs/vuex
v2.1.1
v2.1.0
New
Module Namespacing
Thanks to the great work by @ktsn for bringing us this feature.
Modules can now be auto-namespaced by using the new namespaced: true
option. The getters, actions and mutations inside a namespaced module will automatically be prefixed with a namespaced inferred from the module's registration path. For example:
const store = new Vuex.Store({
modules: {
foo: {
namespaced: true,
// ...
}
}
})
The foo
module will automatically get assigned the namespace foo/
.
Everything inside the module are auto resolved to respect the namespace, so toggling between namespaced or not does not affect the code inside the module itself.
See updated docs for more details.
Helpers for Namespaced Modules
You can now pass a namespace string as the first argument to the mapState
, mapGetters
, mapActions
and mapMutations
helpers, so that the mappings are resolved using that module as the context:
computed: {
...mapState('foo', {
// state is the state of the `foo/` module instead of root
bar: state => state.bar
}
},
methods: {
...mapActions('foo', [
// map this.doSomething() to this.$store.dispatch('foo/doSomething')
'doSomething'
])
}
v2.0.0
v2.0.0-rc.6
New
- Object-style commit is now supported. Note that there's a small difference between 2.0 and 1.0: in 2.0 the mutation objects received by
store.subscribe
will always be in the same format ({ type, payload }
, regardless of whether it was committed via a normal commit or an object-style commit.
Changed
- The logger is now exposed at
vuex/dist/logger.js
instead ofvuex/logger.js
. It is now built as UMD so can be used in browsers directly. It exposes thecreateVuexLogger
global when used via<script>
tags.
Fixed
v2.0.0-rc.5
Fixed
- Avoid destroy watch callback when registering/unregistering modules (@ktsn)
Breaking Changes
-
The
silent
flag for silent dispatches should no longer be part of the payload, but rather declared in a new, 3rd argument options object:// before store.commit('TEST', { silent: true }) // after store.commit('TEST', null, { silent: true })
-
Removed
store.onActionsResolved
as it is found unnecessary.
v2.0.0-rc.4
New
- Now supports unregistering dynamic modules via
store.unregisterModule(path: string | Array<string>)
. Note this method can only unregister modules registered dynamically viastore.registerModule
.
Breaking Changes
store.module
renamed tostore.registerModule
.
v2.0.0-rc.3
Changes from 2.0.0-rc.1
New
-
reverted
store.watch
deprecation. -
New method:
store.onActionsResolved(cb)
Call the callback when all current pending actions (ones that return Promises) have been resolved. This is useful for determining initial data fetch completion during server-side rendering.
-
New helper:
mapState
Directly map store state to computed properties.
computed: { // other properties... ...mapState({ count: state => state.count, withGetters (state, getters) { return state.count + getters.countPlusOne + this.prop // <- also has access to `this` } }) }
-
New helper:
mapMutations
Same as
mapActions
except for committing mutations instead. -
Actions now also have access to getters and root state (for module actions):
actions: { example: ({ state, getters, rootState, commit }) => { // ... } }
-
Getters now also have access to other getters and root state (for module getters):
getters: { example: (state, getters, rootState) => { } }
Fixed
- #243 dynamically registering module should not throw in strict mode
v2.0.0-rc.1
Wasn't 1.0-rc just released?
Yes, the 1.0-rc is consolidating the current API in 0.8.x with very minor breaking changes and marking it as stable. Most current 0.8.x users should be able to easily migrate to 1.0 with minimal effort; However 2.0 introduces some more substantial changes and may not be an easy upgrade for 0.8.x users. We are releasing both in parallel - both will be maintained for bug fixes, the difference being that 1.0 is considered "done" and will not receive new features in the future.
Breaking Changes from 1.0
-
store.dispatch()
is now used for triggering actions instead of mutations. To trigger a mutation, use the newstore.commit()
method. (See Actions & Getters in the Store section below) -
store.dispatch()
andstore.commit()
are no longer variadic: it now accepts a single payload value as the second argument:store.dispatch('action', payload) store.commit('mutation', payload)
-
The
vuex
component option has been deprecated. (See Component Binding Helpers section for new component binding usage below) -
reverted in 2.0.0-rc.3store.watch
is deprecated. Usestore.subscribe
to react to mutations.
Breaking Changes from 0.8.x
-
middlewares
are replaced byplugins
. A plugin is simply a function that receives the store instance:const myPlugin = store => { store.subscribe((mutation, state) => { console.log(`mutation fired: ${mutation.type}`) console.log(`with payload:`, mutation.payload) }) }
New
Actions & Getters in the Store
You can now define actions directly inside the store and trigger them with store.dispatch(type, payload)
:
const store = new Vuex.Store({
state: { count: 0 },
mutations: {
inc: state => state.count++
},
actions: {
incAsync: ({ commit }, delay) => {
setTimeout(() => commit('inc'), delay)
}
}
})
store.dispatch('incAsync', 1000) // increment after 1 second
Same for getters, except you access getters via store.getters[name]
:
const store = new Vuex.Store({
state: {
count: 0
},
getters: {
hasAny: state => state.count > 0
}
})
// access the getter
store.getters.hasAny // -> false
Composable Action Flow
-
To indicate the completion of an action, return a Promise from the action.
store.dispatch
will return that Promise if there is only a single handler called. If multiple action handlers are matched, it will return a Promise that resolves when all Promises returned by those handlers are resolved.const store = new Vuex.Store({ actions: { doSomething: ({ commit }, payload) => { return callPromiseAPI(payload).then(res => { commit('some-mutation', { res }) }) } } }) store.dispatch('doSomething', { id: 1 }).then(() => { // action done })
-
Based on (1) and
async/await
, we can have very clean composition between async actions:const store = new Vuex.Store({ actions: { one: async ({ commit }, payload) => { const res = await callPromiseAPI(payload) commit('some-mutation', { res }) }, two: async ({ dispatch, commit }) => { await dispatch('one') commit('done') } } }) store.dispatch('two') // fires off complicated async flow
The convention of returning Promises also allows Vuex to:
- better handle errors during async action flow.
- simplify store initialization during server-side rendering.
Component Binding Helpers
In Vuex 2.0, the vuex
option will be deprecated in favor of just computed properties and methods. You are free to structure your Vuex store usage the way you prefer. However, we will be keeping the injection for this.$store
so that you can do this:
export default {
computed: {
a () {
return this.$store.getters.a
}
},
methods: {
b (...args) {
this.$store.dispatch('b', …args)
}
}
}
The above alleviates the need to import the store everywhere. But it can get verbose when you have many getters and actions in the same component. Therefore we provide two helpers, mapGetters
and mapActions
:
import { mapGetters, mapActions } from 'vuex'
export default {
computed: mapGetters(['a', 'b', 'c']),
methods: mapActions(['d', 'e', 'f'])
}
So in the component, this.a
maps to this.$store.getters.a
, and this.d(...args)
maps to this.$store.dispatch('d', ...args)
.
If you want to map a getter/action to a different local name, use an object instead:
import { mapGetters, mapActions } from 'vuex'
export default {
computed: mapGetters({
myComputed: 'a' // map this.myComputed to store.getters.a
}),
methods: mapActions({
myMethod: 'b' // map this.myMethod() to store.dispatch('b')
})
}
Finally, you can easily compose them with local computed properties and methods using Object spread operator:
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
localComputed () { … },
...mapGetters(['a', 'b', 'c', 'd'])
},
methods: {
localMethod () { … },
...mapActions(['b'])
}
}
Nested Modules Improvements
Actions, getters and mutations are all supported inside modules.
Similar to how a module mutation receives the sub-state-tree of that module, actions and getters defined in a module will receive the sub-state-tree as well. This makes modules portable without having to be aware of the path it is nested under.
Note that module actions, getters and mutations do all share the same registration namespace with other modules, so if you want to ensure your module is well-isolated, you should consider prefixing its mutation/action types and getter names.
New API methods
-
store.module(path, moduleOptions)
Register a module into the store. The
path
argument can be either a string, or an Array of strings. When registering a nested module, its direct parent state tree must already be present. -
store.subscribe((mutation, state) => { ... })
Subscribe to state changes. Callback is called after each mutation. Typically used in plugins.
Reference
v1.0.0-rc.2
Breaking Changes from rc.1
-
The store instance no longer exposes the event emit interface (
on
,off
,emit
,off
). -
Instead of
store.on('mutation', cb)
, a new methodstore.subscribe
is introduced. Typical usage inside a plugin would be:const myPlugin = store => { store.subscribe((mutation, state) => { // do something... }) }
v1.0.0-rc
Note
Vuex 1.0 marks a stable version with small breaking changes from 0.6~0.8, but should be a relatively easy upgrade for current users.
There are some bigger breaking changes (also big improvements) planned for 2.0 here. Both 1.0 and 2.0 will be maintained in parallel for a reasonable amount of time.
New
-
Nested modules:
A module can in turn contain sub-modules, so a module can be defined as:
type Module = { state?: { [key: string]: any }, mutations?: { [key: string]: Function }, modules?: { [key: string]: Module } }
Thanks to @ktsn for implementing this feature.
-
New method:
store.replaceState(state)
Allows explicitly replacing the store's root state. Note this should only be used for state snapshot restoration / time-travel purposes.
-
The store instance is now also an event emitter with
on
,off
,once
andemit
methods.
Breaking Changes
-
Middlewares are replaced by plugins. A plugin is simply a function that receives the store as the only argument, and can listen to the
mutation
event on the store:const myPlugins = store => { store.on('mutation', (mutation, state) => { // ... }) }
For more details, see the updated docs.
-
Object style dispatch fix: in the docs, Object style dispatch was described as sending the dispatched object into the mutation handlers as the second argument, but in previous versions the implementation was incorrect. This caused confusions such as #184. In 1.0 it's been fixed to reflect what was described in the docs, however this technically is a breaking change.