1. State
Vuex uses a single state tree — a single object that contains all application-level states. At this point it is the “Unique Data Source (SSOT)”. This also means that each app will contain only one Store instance.
Component to obtain the Vuex state
-
Calculate attribute
import store from './store' const Counter = { template: `<div>{{ count }}</div>`.computed: { count () { return store.state.count } } } Copy the code
However, components may need to import stores frequently.
-
Store options
/ / the parent component import store from './store' import Counter from './Counter' new Vue({ el: '#app', store, // "inject" store from the root component into each child component render:c= >c(Counter), }) Copy the code
// Subcomponent counter. vue can be accessed via 'this.$store' :<template> <div>{{count}}</div> </template> <script> export default { computed: { count () { return this.$store.state.count } } } </script> Copy the code
Auxiliary function mapState
When a component needs to fetch multiple states, it can be repetitive and redundant to declare all those states as computed properties. To solve this problem, we can use the mapState helper function to help us generate calculated properties:
//store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)
export default new Vuex.Store({
state: {count:4}})Copy the code
//Counter.vue
<template>
<div>{{sumCount}}</div>
</template>
<script>
import store from './store'
import {mapState} from 'vuex' // Introduce auxiliary functions
export default {
data(){
return{
localCount:7}},computed:mapState({
count:state= >state.count, // Return count () {return this.$store.state.count}
countAlias:'count'.// The alias of count is countAlias, in which case countAlias equals count (note the single quotes).
sumCount(state){ // Handle multiple states: store data plus local data
return state.count+this.localCount
}
})
}
</script>
Copy the code
When the name of the computed attribute of the map is the same as the child node name of state, we can pass mapState an array of strings:
computed: mapState([
'count' Count :state=>state.count,
])
Copy the code
2. Getter
preface
Suppose we need to deal with state in a store, such as filtering and counting lists
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo= > todo.done).length
}
}
Copy the code
However, if many components need the calculated property, we can only copy the function or import it frequently.
Vuex allows us to define “getters” in the Store. Just like evaluating properties, the return value of a getter is cached based on its dependency and recalculated only if its dependency value changes.
application
1. Parameters of the getter
-
getter
acceptstate
As a parameterconst store = new Vuex.Store({ state: { todos: [{id: 1.text: '... '.done: true }, { id: 2.text: '... '.done: false}},getters: { doneTodos: state= > { return state.todos.filter(todo= > todo.done) } } }) Copy the code
-
getter
Accept the second parametergetter
getters: { doneTodos: state= > { return state.todos.filter(todo= > todo.done) } doneTodosCount: (state, getters) = > { return getters.doneTodos.length // Access the doneTodos property through the getter}}Copy the code
2. Access through attributes
/ / access
store.getters.doneTodos
store.getters.doneTodosCount
Copy the code
// used in the component
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
Copy the code
3. Access by method
The getter returns a function that takes parameters to the getter. This is useful for querying arrays in store
getters: {
// ...
getTodoById: (state) = > (id) = > {
return state.todos.find(todo= > todo.id === id)
}
}
Copy the code
/ / access
store.getters.getTodoById(2) // Return the element whose id is equal to the passed id
Copy the code
Helper function mapGetters
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// Mix the getter into a computed object using the object expansion operator. mapGetters(['doneTodosCount'.'anotherGetter'.// ...])}}Copy the code
3. Mutation
A simple example
The only way to change the state in Vuex’s store is to commit mutation. Mutation is similar to events: each mutation has an event type (type) and a callback function (handler). It takes state as the first argument:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
// Event type: increment; The callback function is the latter part;
increment (state) {
state.count++
}
}
})
Copy the code
To call a callback of this event type, use store.mit () and pass in the event type of the string
store.commit('increment')
Copy the code
Payload submission
Additional parameters can be passed to store.mit. These additional parameters are the payload of mutation.
mutations: {
increment (state, n) {
state.count += n
}
}
Copy the code
store.commit('increment'.10)
Copy the code
In most cases, the payload should be an object, which is easier to read:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Copy the code
store.commit('increment', {
amount: 10
})
Copy the code
submission
1. No load
store.commit('Event type')
Copy the code
2. Load:
//
store.commit('increment'.10)
Copy the code
// The payload is an object
store.commit('Event type', {
amount: 10
})
Copy the code
// The payload is an object - object style
store.commit({
type: 'Event type'.amount: 10
})
Copy the code
3. Commit in the component
this.$store.commit('Event type')
Copy the code
MapMutations auxiliary function
methods:{
/ /.... mapMutations(['increment'.$code.store.com MIT ('increment')
'incrementBy'.Use this. Codestore.com MIT ('incrementBy', n) '
'incrementBy2' $store.mit ('incrementBy2',{amount})
]),
...mapMutations({
add:'increment' $store.com MIT ('increment')})}Copy the code
Note: Mutation must be a synchronization function
Use constants instead of Mutation types
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
Copy the code
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: {... },mutations: {
// You can use ES2015 style computed attribute naming to use a constant as a function name
[SOME_MUTATION] (state) {
// mutate state}}})Copy the code
4. Action
preface
Action is similar to mutation, except that:
- The Action commits mutation rather than a direct state change.
- Actions can contain any asynchronous operation.
A simple example
// An instance of adding a random number to an array:
const store = new Vuex.Store({
state: {
msg: []},mutations: {
addMessage(state,msg){
state.msg.push(msg)
}
},
// An asynchronous operation:
actions: {
getMessages(context){
fetch('https://www.easy-mock.com/mock/5f4a32907e1a7f3146e313e7/api/getnum')
.then(res= >res.json())
.then(data= >{
context.commit('addMessage',data.data.number)
})
}
}
})
Copy the code
The Action function accepts a Context object with the same methods and properties as the Store instance, so you can either get state and getters in this object or submit a mutation.
In ES6, you can simplify code with parameter deconstruction
actions: {
getMessages({commit}){
/ /...
commit('addMessage',data.data.number)
/ /...}}Copy the code
Distribution of the Action
1. General distribution
The Action is triggered by the store.dispatch method,
store.dispatch('getMessages')
Copy the code
2. Load distribution
/ / ordinary
store.dispatch('Event type', {
amount: 1
})
// Object form
store.dispatch({
type: 'Event type'.amount: 1
})
Copy the code
3. Distribute it to components
this.$store.dispatch('Event type')
Copy the code
MapMutations auxiliary function
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'getMessages'.// equivalent to 'this.$store.dispatch('getMessages')'
]),
...mapActions({
addmsg: 'getMessages' //this.addmsg equals' this.$store.dispatch('getMessages') '}}})Copy the code
Combination of the Action
How do you know when an action is over? How can you combine multiple actions to handle more complex asynchronous processes? First thing to know:
store.dispatch
Can handle the Promise returned by the handler of the triggered actionstore.dispatch
Still return Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
commit('someMutation')
resolve()
}, 1000)}}}Copy the code
Callbacks can now be specified:
store.dispatch('actionA').then(() = > {
// ...
})
Copy the code
Another action can also specify the callback:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() = > {
commit('someOtherMutation')}}}Copy the code
Finally, with async/await, we can combine actions as follows:
actions: {
async actionA ({ commit }) {
commit('gotData'.await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // Wait for actionA to complete
commit('gotOtherData'.await getOtherData())
}
}
Copy the code
5. Module
preface
When the application becomes very complex, the Store object can become quite bloated. To solve this problem, Vuex allows us to split the Store into modules. Each module has its own state, mutation, action, getter, and even nested submodules:
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 status
store.state.b // -> moduleB status
Copy the code
Local state of a module
const moduleA = {
state: () = > ({
count: 0
}),
For mutation, the first parameter received is the module's local state object.
mutations: {
increment (state) {
state.count++
}
},
For getters, the first parameter exposes local state; The third parameter 'rootState' reveals the rootState
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
//3. For action, the local state is exposed through context.state, and the root node is exposed through context.rootState:
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2= = =1) {
commit('increment')}}}}Copy the code
The namespace
By default, actions, mutations, and getters inside a module are registered in the global namespace — enabling multiple modules to respond to the same mutation or action.
If you want your modules to be more wrapped and reusable, you can add Namespaced: True to your module objects to make them namespaced. When a module is registered, all its getters, actions, and mutations are automatically named according to the path the module was registered with
const store = new Vuex.Store({
modules: {
account: {
namespaced: true.state: () = >({... }),getters: {isAdmin () { ... }},// Access from store.getters['account/isAdmin'
actions: {login () { ... }},// Dispatch via store.dispatch('account/login')
mutations: {login () { ... }},// Submit via store.mit ('account/login')
// A nested module in a module - inherits the parent module's namespace
modules: {
1 - myPage / / module
myPage: {
state: () = >({... }),getters: {profile () { ... }}//store.getters['account/profile']
},
// module 2-mposts- further nested namespace
posts: {
namespaced: true.state: () = >({... }),getters: {popular () { ... }}//store.getters['account/posts/popular']}}}}})Copy the code
Access global content within a module with a namespace
RootState and rootGetters are passed into the getter as the third and fourth arguments, and the action is passed into the context object’s properties.
To distribute action or commit mutation within the global namespace, pass {root: true} as the third argument to Dispatch or COMMIT.
modules: {
foo: {
namespaced: true.getters: {
someOtherGetter: state= >{... }For the getter, use the fourth argument 'rootGetters' to access the root getter
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> local
rootGetters.someOtherGetter // -> global}},actions: {
someOtherAction (ctx, payload) { ... }
//2. For actions, accept the root attribute to access root Dispatch or commit
someAction ({ dispatch, commit, getters, rootGetters }) {
dispatch('someOtherAction') // -> Partial distribution
dispatch('someOtherAction'.null, { root: true }) // -> Global distribution
commit('someMutation') // -> Local commit
commit('someMutation'.null, { root: true }) // -> Global commit}}}},Copy the code
Register global actions in namespaced modules
Add root: true to the action and put the action definition in a function handler. Such as:
{
/ /...
modules: {
foo: {
namespaced: true.actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> define someAction action
}
}
}
}
}
Copy the code
A binding function with a namespace
When using functions such as mapState, mapGetters, mapActions, and mapMutations to bind a module with a namespace:
computed: { ... mapState({a: state= > state.some.nested.module.a,
b: state= > state.some.nested.module.b
})
},
methods: {
...mapActions([
'some/nested/module/foo'.// -> this['some/nested/module/foo']()
'some/nested/module/bar' // -> this['some/nested/module/bar']()])}Copy the code
You can pass the module’s space name string as the first argument to the above function, so that all bindings automatically use the module as the context, simplifying:
computed: { ... mapState('some/nested/module', {
a: state= > state.a,
b: state= > state.b
})
},
methods: {
...mapActions('some/nested/module'['foo'.// -> this.foo()
'bar' // -> this.bar()])}Copy the code
Another way to simplify things: use createNamespacedHelpers to create namespace-based helper functions. It returns an object containing the new component binding helper function bound to the given namespace value:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')// Based on a name
export default {
computed: {
...mapState({
a: state= > state.a,
b: state= > state.b
})
},
methods: {
...mapActions([
'foo'.'bar'])}}Copy the code
Example source code