Why do we need Vuex
First of all, the official definition of VUE for VUEX is:
Vuex is a state management mode developed specifically for vue.js applications.
The state management mode, which may be a little abstract to understand, is a way to solve the data sharing of various components in the VUE framework. It is similar to the global data warehouse. The state object maintained in VUEX can be processed to each VUE instance. So what kind of scene was it made for?
If you know about vUE framework, you know that VUe2.0 is to pass in an option to generate vUE instance in the way of options. Each option needs to define a data attribute, or calculation attribute. This internal maintained data can be bound to the view, namely MVVM bidirectional binding.
If application is relatively simple, there is no design data sharing between multiple vue component, a data can also be maintained internally vue can be resolved if the design of multiple components of data sharing, so obviously and not up to the scene, although the vue framework internal communication technology also provides several components, For example, parent and child components communicate through props/emit, but it is still not competent for complex scenarios requiring multiple components to share data. So Vuex was born.
At the heart of every Vuex application is the Store. A “store” is basically a container that contains most of the states in your app. Vuex differs from a purely global object in two ways:
- Vuex’s state storage is reactive. When the Vue component reads the state from the Store, if the state in the store changes, the corresponding component is updated efficiently accordingly.
- You can’t just change the state in the store. The only way to change the state in a store is to commit mutation explicitly. This allows us to easily track each state change, which allows us to implement tools that help us better understand our application.
What are the components of vuEX
state
As mentioned above, the essence of Vuex is to maintain a global database that can be shared across applications. This requires the inclusion of a data container, which in VUEX corresponds to a State object that maps a set of globally shared data in the form of key values.
For example, let’s say we want to implement a counter application that wants its count to be shared globally. You can start by defining a state container. Introduce vuex and instantiate the Store instance.
Const state = {todos: [{text: 'cook ', done: true}, {text:' cook ', done: false}]}, const state = {todos: [{text: 'cook ', done: true},Copy the code
State objects are now available in any VUE component via store.state.
getter
Vuex allows us to derive states from state in the store that can be placed under a getters object. Think of it as a computed property of a store. Just like a computed property, the return value of a getter is cached based on its dependency and is recalculated only if its dependency value changes.
For example, there is a scenario where the list is filtered and counted: without the getters attribute, it looks like this
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
Copy the code
If more than one component needs this property, we can either copy the function or extract a shared function and import it in multiple places — neither approach is ideal.
For brevity, this part of the logic can be placed in getters, which accepts state as its first argument, and getters can accept other getters as its second argument.
const getters = {
count: 0,
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
Copy the code
These variables can now be accessed in the project via store.getters. DoneTodos
mutation
With the ease of data, VUex has its own rules for data maintenance, i.e. changes to data. Vue officials emphasized that we submitted mutation instead of directly changing store.state.todo because we wanted to track state changes more explicitly. This simple convention makes your intentions clear, making it easier to interpret in-app state changes as you read the code. In addition, this gives us the opportunity to implement debugging tools that record every state change and save a snapshot of the state.
The way to define mutations is also very simple, as long as the change of the corresponding attributes of state is encapsulated as a method and placed in the mutations object
const mutations = {
setCount (state, value) {
state.count = value
},
addTodo(state, item){
state.todo.push({
text: item,
done: false
})
}
}
Copy the code
Note that mutation must be a synchronous function if the mutation function contains asynchronous logic, as shown below
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
Copy the code
We are debugging an app and looking at the mutation log in devtool. Each mutation is recorded, and DevTools needs to capture a snapshot of the previous state and the next state. However, the callback in the asynchronous function in mutation in the example above made this impossible: Because the callback had not yet been called when mutation was triggered, DevTools did not know when the callback was actually called.
Distribution of Mutation
Once mutations are defined, a state change can be triggered using the store.mit method.
store.commit('setCount', 10)
Copy the code
There is also support for another method of submission via object style
store.commit({
type: 'increment',
amount: 10
})
Copy the code
action
We have just mentioned that mutation cannot include the logic of state changes caused by asynchronous methods, which would make the state of the program difficult to debug. So vuex needs to separate the state changes caused by these two methods, so vuex provides another object for asynchronous state changes –action. Action is similar to mutation, except that:
- The Action commits mutation rather than a direct state change.
- Actions can contain any asynchronous operation.
The Action function accepts a Context object with the same methods and properties as the store instance. This context contains the same properties as the store instance, such as state,getters, or a mutation call can be made via the commit method.
const actions = {
setCount (context, value) {
context.commit('setCount', value)
}
}
Copy the code
In practice, we’ll often use ES2015 parameter deconstruction to simplify code (especially if we need to call commit many times) :
const actions = {
setCount ({ commit }, value) {
commit('setCount', value)
}
}
Copy the code
In addition to committing a COMMIT within the action, asynchronous logic can also be performed within the action
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Copy the code
Distribution of the Action
Action is triggered by the store.dispatch method:
store.dispatch('increment')
Copy the code
Actions support the same object approach for distribution:
// Distribute store.dispatch({type: 'incrementAsync', amount: 10}) as an objectCopy the code
How to use Vuex
Before using Vuex, you need to import it and download it via NPM
npm install vuex --save
Copy the code
After downloading vuex, you need to register the component globally within the Vue application, that is, explicitly install vuex: index.js via vue.use ()
import Vue from 'vue'
import Vuex from 'vuex'
import { state, getters, mutations, actions } from './state'
Vue.use(Vuex)
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
})
Copy the code
This maintains a global data state management for the entire VUE application. It defines the global data state, as well as a series of mutations and actions that provide synchronous and asynchronous updates for these status updates.
Auxiliary function
Vuex provides several helper functions to make it easier to use this global state data in components.
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:
import { mapState } from 'vuex' export default { // ... Computed: mapState({// Arrow function makes code more concise count: State => state.count, // Pass string argument 'count' equal to 'state => state.count' countAlias: CountPlusLocalState (state) {return state.count + this.localcount}})}Copy the code
We can also pass mapState an array of strings when the name of the computed property of the map is the same as the name of the child node of State. This is also a common usage scenario in projects
Computed: mapState([// map this.count to store.state.count 'count'])Copy the code
It is worth noting that the mapState function returns an object. You can use the object expansion operator when you need to use mapState with local data from the component itself
computed: { localComputed () { /* ... */}, // Use the object expansion operator to blend this object into an external object... mapState({ // ... })}Copy the code
mapGetters
The mapGetters helper function simply maps the getters in the store to local computed properties:
mport { mapGetters } from 'vuex' export default { // ... Computed: {// Mix getters into a computed object using the object expansion operator... mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ] )}}Copy the code
Similarly, if you want to give a getter property another name, use the object form:
MapGetters ({/ / the ` enclosing doneCount ` mapping for ` enclosing $store. The getters. DoneTodosCount ` doneCount: 'doneTodosCount})Copy the code
mapMutations
Use this. codestore.mit (‘ XXX ‘) to commit mutation. The Mutations method in VUEX also provides the mapMutations helper function to map the methods in the component to the store.mit call.
import { mapMutations } from 'vuex' export default { // ... methods: { ... Apply mutations ([' increments ', // map 'this.increment()' to 'this.store.com MIT (' increments ')' 'incrementBy' // Map 'this.incrementBy(amount)' to 'this.incrementBy(amount)'),... Apply mutations ({add: 'increment' // map 'this.add()' to 'this.store.com MIT ('increment')'})}}Copy the code
mapAction
Vuex also provides mapActions helper functions to map a component’s methods to a store.dispatch call.
import { mapActions } from 'vuex' export default { // ... methods: { ... MapActions (['increment', // Map 'this.increment()' to 'this.$store.dispatch('increment')' // 'mapActions' also supports payloads: 'incrementBy' // map 'this.incrementBy(amount)' to 'this.$store.dispatch('incrementBy', amount)']),... MapActions ({add: 'increment' // map 'this.add()' to 'this.$store.dispatch('increment')'})}}Copy the code
module
Because of the use of a single state tree, all the states of an application are grouped into one large object. When the application becomes very complex, the Store object can become quite bloated.
To solve these problems, Vuex allows us to split the Store into modules. Each module has its own state, mutation, action, and getter.
onst 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 statusCopy the code