Supporting executable code example = > GitHub
Vuex first met
Vuex : a state management mode specially developed for Vuejs applications, which is applied to multi interface state management. It uses centralized storage to manage the state of all components of the application, and uses corresponding rules to ensure that the state changes in a predictable way.
State management: it can be simply understood as storing variables that need to be shared by multiple components in an object, and then placing the object in the top-level Vue instance so that other components can use it.
Encapsulating an object by yourself can also realize state management, but it is more troublesome to make all attributes responsive. Vuex is to provide such a plug-in to share state among multiple components.
Vuex's basic idea: Global singleton mode. Extract the states that need to be shared and manage them uniformly. After that, each view is accessed and modified according to the specified format. Vuex is a tool to provide unified management.
What states do we need to share among multiple components?
- User's login status (token), user name, avatar, geographical location, etc
- Items in collection and shopping cart
Vuex installation: npm install vuex --save
Vuex core concepts: State, Getters, movements, Actions, Modules
Vuex basic usage
First, create and export the Vuex.Store instance in the store/index.js file
import Vuex from 'vuex' //1. Vue.use (plug-in): install the Vuex plug-in Vue.use(Vuex) //2. Create Vuex.Store instance const store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, modules: {} }) //3. Export instance export default store
Step 2: mount the created store instance in the Vue instance of the main.js file
import Vue from 'vue' import App from './App' import store from './store' new Vue({ el: '#app', store, render: h => h(App) })
Third, any component in the project can access the instance through $store
<h2>{{$store.state.counter}}</h2>
Vuex-State
If the state information is saved to multiple Store objects, the subsequent management and maintenance will become particularly difficult. Therefore, Vuex uses a Single Source of Truth (single data source) to manage all the states at the application level. In other words, a Store in a project is OK.
//store/index.js state: { counter: 0, students: [ {name: 'Perry', age: 19}, {name: 'Bob', age: 20}, {name: 'Alice', age: 21} ] }
Vuex-Getters
Calculated properties of similar components. Sometimes you need to get some processed states from the Store, so you can use Getters.
- The first parameter of the getters method is state, which is not passed when the component is used. The default is $store.state, that is, the state of the current store instance
- The second parameter of the getters method is getters, which does not need to be passed when the component is used. The default is $store.getters, that is, the getters of the current store instance
- If the getters method needs custom parameters, it can return a parameterized function in the method. When the component is used, it is equivalent to calling this function and passing the corresponding parameters
//store/index.js getters: { powerCounter(state) { return state.counter * state.counter }, more20stu(state) { return state.students.filter(s => s.age>20) }, more20stuLength(state, getters) { return getters.more20stu.length }, moreAgeStu(state) { return age => { return state.students.filter(s => s.age>age) } } }
Use in components:
<h2>{{$store.getters.powerCounter}}</h2> <h2>{{$store.getters.more20stu}}</h2> <h2>{{$store.getters.more20stuLength}}</h2> <h2>{{$store.getters.moreAgeStu(18)}}</h2>
Vuex-Mutations
Devtool is a Vue developer tool. If Vuex's State is modified by submitting Mutation, devtool can track the State change process; If the State is manipulated directly, it cannot be tracked. Therefore, the only way to officially specify the update of Vuex's Store status is to submit changes.
Movements mainly consists of two parts:
- Event type, that is, the name of the callback parameter submitted and defined
- Callback function (handler), the first parameter is state
//store/index.js mutations: { increment(state) { state.counter++ } }
Use in components:
methods: { add() { this.$store.commit('increment') } }
Variables pass parameters
When updating data through changes, you sometimes want to carry some additional parameters, which are called the loads of changes. If multiple parameters need to be passed, they are usually passed in one object, that is, the Payload is an object, and then the relevant information is taken from the object.
//store/index.js mutations: { incrementCount(state, count) { state.counter += count }, addStu(state, stu) { state.students.push(stu) } }
Use in components:
methods: { addCount(count) { this.$store.commit('incrementCount', count) }, addStu() { const stu = {name: 'Mary', age: 23} this.$store.commit('addStu', stu) } }
Summarize the parameters of the mutations method:
- The first parameter is state, which does not need to be passed when the component is used. The default is $store.state, that is, the state of the current store instance
- The second parameter is payload, which is the information carried by the component when submitting events through commit. It is an optional parameter
Changes submission style
The above is a common submission style. Vue also provides another style: it is an object containing the event type type attribute. The processing method in mutations is to treat the entire commit object as a payload.
//store/index.js mutations: { incrementCountSpec(state, payload) { state.counter += payload.count } }
Use in components:
methods: { addCount(count) { this.$store.commit({ type: 'incrementCountSpec', count }) } }
Changes response rule
Vuex's state is responsive: the attribute defined in state will be added to the responsive system, and the responsive system will listen for attribute changes. When the attribute changes, it will notify all components where the attribute is used to update the interface.
For more information on Vue's responsive principle, see this note: Brief introduction of Vue responsive principle
But this requires us to follow some Vuex rules: only properties initialized in state are responsive; When adding a new property or deleting a property to an object in state, you need to use Vue.set() or Vue.delete(), otherwise it is not responsive.
Vue.set(state.info, 'address', 'Nanjing') Vue.delete(state.info, 'height')
Constant of type Mutations
In mutations, we define many event types, that is, method names. When there are too many methods, you need to spend a lot of energy to remember these methods, and you may make mistakes. Therefore, it is best to define event types as constants.
Create the changes-types.js file in the store directory to export constant event types:
//store/mutations-types.js export const INCREMENT = 'increment'
In store/index.js:
//store/index.js import { INCREMENT } from './mutations-types' mutations: { [INCREMENT](state) { state.counter++ } }
Use in components:
import { INCREMENT } from './store/mutations-types' methods: { add() { this.$store.commit(INCREMENT) } }
Vuex-Actions
Vuex requires that the methods in mutations must be synchronized. Devtools can help us capture snapshots of mutations. However, if it is an asynchronous operation, devtools will not be able to track the state change of the operation well.
But in some cases, we do want to do some asynchronous operations in Vuex, such as network requests. Actions is similar to movements and is used to perform asynchronous operations instead of movements.
Parameters of actions method:
- The first parameter is context, which does not need to be passed when the component is used. The default is $store, that is, the current store instance
- The second parameter is payload, which is the information carried by the component when submitting events through dispatch. It is an optional parameter
//store/index.js actions: { aUpdateInfo(context, payload) { return new Promise(((resolve, reject) => { setTimeout(() => { context.commit('updateInfo') console.log(payload) resolve('Parameters for successful callback') }, 1000) })) } }
Use in components:
updateInfo() { this.$store .dispatch('aUpdateInfo', 'Carry information') .then(res => { console.log('success'); console.log(res); }) }
Note: this.$store.dispatch is equivalent to the Promise returned by calling the aUpdateInfo method, so it is followed by then.
For more information about Promise objects and other common ES6 syntax, see this note: ES6 common syntax summary
Vuex-Modules
Vuex uses a single state tree, which means that all States will be managed by one state. When the application becomes very complex, the state may become quite bloated. To solve this problem, vuex allows us to divide the store into modules. Each module has its own state, changes, actions, getters, etc.
//store/index.js const moduleA = { state: { name: 'A' }, mutations: { updateName(state, payload) { state.name = payload } }, actions: { //context: including the commit, getters, rootState, etc. of the current level module aUpdateName(context) { setTimeout(() => { context.commit('updateName', 'zhangsan') }, 1000) } }, getters: { fullname(state) { return state.name + '111' }, //Getters: get the getters of the current level module, rootState: get the state of the superior module fullname2(state, getters, rootState) { return getters.fullname + rootState.counter }, } } const moduleB = { ... } const store = new Vuex.Store({ state: { counter: 0 }, modules: { a: moduleA, b: moduleB } })
Use in components:
<template> <h2>{{$store.state.a.name}}</h2> <h2>{{$store.getters.fullname}}</h2> <h2>{{$store.getters.fullname2}}</h2> <button @click="asyncUpdateName">Asynchronous update name</button> <button @click="updateName">to update name</button> </template> <script> updateName() { this.$store.commit('updateName', 'frog') }, asyncUpdateName() { this.$store.dispatch('aUpdateName') } </script>
Note: use $store.state. Module name and property name in the component to obtain the properties in the module. When using getters, commit and dispatch, it is not necessary to indicate which module's properties or methods are used.
Vuex store directory structure
When there are a lot of getters, variations and actions, you can extract each part into a file and reconstruct the store directory:
//store/index.js import moduleA from './modules/moduleA' import moduleB from './modules/moduleB' import mutations from './mutations' import actions from './actions' import getters from './getters' const state = { ... } const store = new Vuex.Store({ state, mutations, getters, actions, modules: { a: moduleA, b: moduleB } }) export default store
//store/mutations.js export default { ... }
//store/modules/moduleA.js export default { state: {...}, mutations: {...}, actions: {...}, getters: {...} }