brief introduction
Vuex is Vue JS application state management mode + library. It acts as a centralized storage for all components in the application, and its rules ensure that the state can only be changed in a predictable way.
In short, it provides a library of state management, and the change of state is predictable.
Status: I personally understand that any variable in your component can be a status. As long as you want, you can write it into the state, but I'm sure you won't.
install
- For existing projects, you can use vue add vuex to add vuex.
- When creating a project, you can select Manually select features to customize the configuration.
- npm install vuex@next --To save, you need to create a store file manually. The first two are recommended.
After installation, a store folder will be created under the src file of the project, with an index file inside
// index.js import { createStore } from 'vuex' export default createStore({ state: {}, getters: {}, mutations: {}, actions: {}, modules: {} })
vuex4. In order to keep consistent with vue3's creation method, it is recommended to use createStore to create X
Core concept
state
Store data
Add a variable count to state. At this time, count will be accessible in any component under the project. The data in state is called state. Storing data in state is the same as storing data in data memory.
state(){ count: 0 }
Access count in component
- $store. In template state. count
- This. In object$ store. state. count
Since Vuex stores are reactive, the easiest way to retrieve status from them is to simply return some store status from calculated properties:
computed(){ count(){ return this.$store.state.count; } }
At this point, you can access the data count in the store by calculating the attribute count. Whenever the count in the store is modified, it will cause the calculation property to be recalculated and trigger the associated DOM update.
If too many states need to be introduced, the code will obviously become repetitive and lengthy when Binding calculation attributes to each state. To solve this problem, import the mapState method.
mapState can help us generate the calculated state function.
mapState
You need to import mapState module from vuex
import { mapState } from 'vuex'
There are two ways to use mapState
// Incoming object form computed: mapState({ // First kind count: state => state.count, // The second: passing the string 'count' is equivalent to state = > state count countAlias: 'count', // The third method: define localcount in data in combination with the current component status countPlusLocalState(state){ return state.count + this.localcount; } })
mapState when the name of the mapped calculated attribute is the same as the name of the state subtree, we can also pass a string array.
computed: mapState([ 'count' ])
When visiting
this.count
The computed of the store is combined with the computed of the component
If the calculated attribute of the current component is used together with the calculated attribute of the store, you need to use the object extension operator.
mapState returns an object
computed: { localCountDecorate(){ return `Local component status ${this.localcount}` }, // You can pass an array or an object ...mapState([ 'count' ]) }
The component calculation properties written in mapState are also valid, but the parameters accepted are different, so they need to be separated
Components can still have local state
Using Vuex does not mean that you should put all States into Vuex. While adding more states to Vuex can make your state transitions more explicit and debuggable, sometimes it can also make your code more verbose and indirect. If a state strictly belongs to a single component, it is OK to keep it local.
getters
getters can be called decorators. They can do a layer of processing (decorating / filtering) on the data in the state and return it to the component.
First, create a status (product list) in state
articleList: [ {id: '001', articleName: 'xinjiang cotton', description: 'Xinjiang is rich in high-quality long staple cotton', stock: 1000000, price: 100}, {id: '002', articleName: 'shoe', description: 'LiNing Wade's way 4', stock: 10, price: 100}, {id: '003', articleName: 'Graphics card', description: '3080 Series graphics card', stock: 0, price: 100}, {id: '004', articleName: 'vaccine', description: 'New Coronavirus vaccine', stock: 100000000, price: 100}, {id: '005', articleName: 'xiaomi 11', description: 'xioami 11th generation mobile phone', stock: 100000, price: 100}, {id: '006', articleName: 'Kirin Chip', description: 'Huawei Kirin chip', stock: 0, price: 100} ]
Filter them in getters
getters: { Instock(state, getters){ // state as the first parameter, getters accesses other getters console.log(getters); return state.articleList.filter(item => item.stock != 0); } }
getters receives two parameters
- State: access the state in the current store
- Getters: access other getters in the current store
Now filter the status articleList in the state once, and remove all the items with stock 0.
Access in component
The access method is the same as state
computed: { Instock(){ // You may not use the same name return this.$store.getters.Instock; } }
Similarly, mapGetters can be used to map to computed to reduce the amount of code.
mapGetters
The rule is the same as mapState
computed: { ...mapState(['Instock']) }
When visiting
this.Instock()
Get specified data
getters can return a function to receive the input of a parameter for finding data.
// getters searchArticle: (state) => (id) => { // The return function passes in the id and uses the id to find the data return state.articleList.filter(item => item.id == id); }
In component
...mapGetters([ 'Instock', 'searchArticle' ])
At this time, you can pass parameters to return the specified data
this.searchArticle('001'); // Specify the data whose return id is 001
getters only has the ability to decorate and return the state in the state, and cannot directly change the state. Changes are required to change state.
mutations
State changes in state can only be made in changes. And asynchronous operations cannot exist in mutations
mutations: { addCount(state, payload){ // Parameter accumulation executed in the receiving component state.count += payload.count; } }
Two parameters are acceptable
- State: access the state of the current store
- payload: parameter passed. In most cases, it should be an object to pass more state
Declared in methods in component. Changes submit changes through commit.
// template <button @click="addCount">increase</button> // js methods: { addCount(){ this.$store.commit('addCount', {count: 1}); // Parameters passed by {count: 1} } }
Simplification: mapMutations
import { mapMutations } from 'vuex';
methods: { // Pass array ...mapMutations(['addCount']) // this.addCount() execution // Transfer object (assign new value) ...mapMutations({ localtotal: 'addCount' }) // this.localtotal() execution }
In some cases, it is inevitable to make requests or other asynchronous operations when changing data, and asynchronous operations are not allowed in mutations. actions will be used
actions
Actions are similar to mutations, except that:
- The action does not change the state, but performs the change.
- Actions can contain any asynchronous operation.
Actions does not have the right to change the state, but can commit changes, so actions only performs changes.
actions: { asynCount(context, products){ setTimeout(() => { context.commit('addCount', products); }, 1000); } }
Receive two parameters
- Context: store context object. Deconstruction can be used, including commit, dispatch, getters, rootGetters, state and rootState
- products: parameters passed
Call in component. actions are triggered by dispatch
// template <button @click="asynAddCount">Add one after one second</button> // js methods: { asynAddCount(){ this.$store.dispatch('asynCount', { count: 3 }); } }
Simplify: mapActions
import { mapActions } from 'vuex';
methods: { // Pass array ...mapActions(['asynCount']) // this.asynCount() execution // Transfer object ...mapActions({ localAsyncTotal: 'asynCount' }) // this.localAsyncTotal() execution }
mudeles
As our apps grow in size, the store may become bloated. To solve this problem, Vuex allows us to divide the store into modules. Each module can contain its own state, variation, action, getter, or even nested modules - the whole process is fractal
In the case of larger and larger projects, it is particularly necessary to divide modules.
The file is deconstructed as follows
store └─ index.js └─ moduleA.js └─ moduleAInside.js └─ moduleB.js
Code in file
// index.js import { createStore } from 'vuex' import moduleA from './moduleA' import moduleB from './moduleB' export default createStore({ state: { count: 0, }, getters: { }, mutations: { addCount(state, payload){ state.count += payload.count; } }, actions: { asynCount(context, products){ setTimeout(() => { context.commit('addCount', products); }, 1000); } }, modules: { moduleA, moduleB } })
// moduleA.js import moduleAInside from './moduleAInside' const moduleA = { state: () => ({ moduleAstate: 'unknown' }), getters: { ... }, mutations: { ... }, actions: { ... }, modules: { moduleAInside } } export default moduleA;
// moduleAinside.js const moduleAInside = { state: () => ({ moduleAInsideState: 'unknown' }), getters: { ... }, mutations: { ... }, actions: { ... } } export default moduleAInside;
// moduleB.js const moduleB = { state: () => ({ moduleBstate: 'unknown' }), getters: { ... }, mutations: { ... }, actions: { ... } } export default moduleB;
The usage inside the module is the same as that outside. Similarly, modules can be nested inside the module.
About accessing other modules or root modules within a module.
- *** * the third module root, gets root and the fourth module root can be accessed
- **Actions: * * the context of actions contains rootState and rootGetters, which can be used to access other modules and root modules.
senior
vue3 introduces a composite api in which you can use the useStore function to create a reference to vuex, which is equivalent to this$ store
import { useStore } from 'vuex' export default { setup(){ const store = useStore(); } }
Access state and getters
Since I access the status in the comAPI module, I need to access the module before accessing the specific status when accessing the state, and getters is not required.
const comState = computed(() => store.state.comAPI.comAPIState); const comGetters = computed(() => store.getters.comAPIGetters);
Access changes and actions
Accessing changes and actions does not need to consider the scope of the module.
const changeState = () => { store.commit('changeState') } const asynChangeState = () => { store.dispatch('asynChangeState') }
Compare vuex4 & vuex3
Creation method
vuex4 and vue3 are created in the same way
import { createStore } from 'vuex' export const store = createStore({ ... })
Install to vue
import { createApp } from 'vue' import { store } from './store' import App from './App.vue' const app = createApp(App) app.use(store) app.mount('#app')
New combined API useStore
import { useStore } from 'vuex' export default { setup () { const store = useStore() } }