[learning notes] learn to use Vuex

Posted by Bauer418 on Sun, 16 Jan 2022 19:19:45 +0100

Introduction, installation and initialization

What is vuex

VueX is a state management tool for use in Vue project development. Vue provides a unified management tool - VueX for these values frequently used by multiple components. In a Vue project with VueX, we only need to define these values in VueX, which can be used in the components of the whole Vue project.

How to install vuex

npm installation

npm i vuex -s

How to use vuex

Add a new store folder under the root directory of the project, and create index js

This is the src folder of the project

│  App.vue
│  main.js
│
├─assets
│      logo.png
│
├─components
│      HelloWorld.vue
│
├─router
│      index.js
│
└─store
       index.js

In the store JS file, introduce vuex and use vuex. Note that the variable names are uppercase Vue and vuex

//store.js
import Vue from 'vue'
import Vuex from 'vuex'
//Mount Vuex
Vue.use(Vuex)
//Creating VueX objects
const store = new Vuex.Store({
    state:{
        //The stored key value pair is the state to be managed
        name:'helloVueX'
    }
})
export default store

Mount the store to the Vue instance of the current project

//main.js
import store from './store'
new Vue({
  el: '#app',
  router,
  store,  // Like router, mount the Vuex instance we created into this vue instance
  render: h => h(App)
})

Using Vuex in components

For example, in app In Vue, we will display the name defined in state in the h1 tag

<template>
    <div id='app'>
        name:
        <h1>{{ $store.state.name }}</h1>
    </div>
</template>

Or use it in component methods

methods:{
    add(){
      console.log(this.$store.state.name)
    }
},

The specific usage will be described in detail below

Note: the emergence of vuex is to solve the communication problem between components. If an operation or data does not involve a public operation, but only a single component operation, do not store these status values or function s in vuex, because vuex will mount itself to all components, no matter whether the current component uses the contents or not, so this will in fact increase the loss of performance

Core content in VueX

In vuex, there are five basic objects by default:

  • State: storage state (variable)
  • getters: recompilation before data acquisition can be understood as the calculation attribute of state.
  • Changes: changes the state and is synchronized. This is similar to the custom event in our component.
  • actions: asynchronous operations.
  • modules: a sub module of the store

Split into single file

If there are too many states and methods in the project, index JS files will look bloated and difficult to maintain. At this time, we can split state, getters, variations and actions into a single file, which is conducive to management

Here is the directory structure

store
│      
│
├─index.js
│      
│      
├─state.js
│      
│
├─getters.js
│      
│
├─mutations.js
│      
│
└─actions.js

index.js

import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import getters from './getters';
import mutations from './mutations';
import actions from './actions';
Vue.use(Vuex);
export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
});

In other files, you only need to export

state.js

export default {
  name:'hzw'
};

mutations.js

export default {
 changeName(state, name) {
    state.name = name;
  },
};

getters.js

export default {
 realName(state) {
    return "full name:" + state.name
  },
};

actions.js

export default {
 changeName({ commit }, name) {
        return commit('changeName', name)
    }
};

This makes it look more structured and easier to maintain

state and mapState

What is state

state(vuex) ≈ data (vue)

vuex's state and vue's data have many similarities. They are used to store some data, or state values These values will be loaded with two-way binding events between data and dom, that is, when the value changes, the dom update can be triggered

We're in state JS declares a state count with an initial value of 0, and then outputs it in the component

// state.js 
export default {
  count:'0'
};
//In component
<template>
  <div class="hello">
    <h3>{{$store.state.count}}</h3>
  </div>
</template>

The results are shown in the figure below

Note: Although there are many similarities between state and data, state is usually attached to the calculated calculation attribute of the sub component when it is used, which is conducive to responding to the sub component in time when the value of state changes If you use data to receive $store State can also receive values, but since this is only a simple assignment operation, the data in vue cannot listen to the change of state in state You can also use watch $store to solve this problem, but it's a little troublesome

Therefore, it is better to use computed to receive the state. As follows, the method of modifying the state will be learned later, and will be shown here first

//mutations.js
export default {
  add(state, n = 0) {
    return (state.count += n)
  },
  reduce(state, n = 0) {
    return (state.count -= n)
  }
}
//In component
<template>
  <div class="hello">
    <h3>{{$store.state.count}}</h3>
    <div>
      <button @click="add(10)">increase</button>
      <button @click="reduce(10)">reduce</button>
      <div>computed:{{dataCount}}</div>
        <div>data: {{count}}</div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
   data () {
    return {
      dataCount: this.$store.state.count //Receive with data
    }
  },
  computed:{
    count(){
      return this.$store.state.count //Receive with computed
    }
  },
  methods: {
    add(n){
      this.$store.commit('add',n);
    },
   reduce(n){
      this.$store.commit('reduce',n);
    }
  }
}
</script>

Then we click the Add button to see what happens. The results are as follows

It can be seen that the value received with data cannot respond to the update in time. Just use computed

Knowledge point: the initialization of Props, methods,data and computed is completed between beforeCreated and created.

What is mapState

Surface meaning: mapState is an auxiliary function of state

Practical effect: when a component needs to obtain multiple states, declaring these states as calculation attributes will be somewhat repetitive and redundant. To solve this problem, you can use the mapState helper function to help generate calculated properties

Usage: first import this auxiliary function

import { mapState } from 'vuex'

Then you can use mapState in computed

When using auxiliary functions such as mapState, if the name inside the component is consistent with that in vuex, it can be abbreviated to array.

//state.js
export default {
    nickname:'Simba',
    age:20,
    gender:'male'
};
//computed
computed: mapState(['nickname','age','gender'])
//The above sentence of code is equivalent to the following. Is it a lot simpler
computed:{
  nickname(){return this.$store.state.nickname}
  age(){return this.$store.state.age}
  gender(){return this.$store.state.gender}
}

If you need to customize a calculation property, you need the expansion operator in es6:

data(){
  return{
    count:14
  }
}
computed: {   
  value(){
   return "full name:" + this.coount/7
},
  ...mapState(['nickname','age','gender'])
}

getters and mapGetters

What are getters

Getters: for recompilation before data acquisition, the return value of getters will be cached according to its dependency, and will be recalculated only when its dependency value changes. To put it bluntly, it is about equal to vue's computed. You can use getters like computed. Of course, there are differences between the two

How to use getters

The method in getters has two default parameters

  • State the state object in the current VueX object
  • Getters the current getters object, which is used to use other getters under getters
//state.js
export default {
  name:'simba',
  age:'20'
};
//getters.js
export default {
  // The first parameter is state
  realName(state) {
    return "full name:" + state.name
  },
  // The second parameter can access getters
  nameAndAge(state, getters) {
    return "Age:" + state.age +";"+ getters.realName
  }
};

How to access getters

Access through properties

getter will be exposed as store Getters object, we can access these values in the form of attributes:

store.getters.realName// ->Name: simba

Note: getter s are cached as part of Vue's responsive system when accessed through attributes.

Access by method

We can pass parameters to getters by asking getters to return a function. This is very useful when querying the array in the store.

state:{
  todos:[
    {
      id:2,
      text:'...',
      done: false
    }
  ]
},
getters: {
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

Note: when accessing a getter through a method, it will be called every time without caching the results.

Use in components

We passed this in computed$ store. getters. XXX to bind a calculation property

//In component
<template>
  <div class="hello">
    <div>
        <div>{{message}}</div>
        <div>{{message2}}</div>
    </div>
  </div>
</template>
computed:{
   message(){
     return this.$store.getters.realName 
   },
   message2(){
     return this.$store.getters.nameAndAge; 
   }
},

The results are as follows:

What is mapGetters

The mapGetters auxiliary function only maps getters in the store to local calculation properties:

Usage: first import this auxiliary function

import { mapGetters } from 'vuex'

Then you can use mapGetters in computed

computed: {
  ...mapGetters({
    message: "realName",
    message2: "nameAndAge"
  })
},

Is it a lot simpler? If the name of the calculated attribute is the same as that of getters, you can also use the shortened form of array

computed: {
  ...mapGetters(["realName","nameAndAge"])
},

mutation and mapMutation

What is mutation

mutation is a collection of methods for operating state data, such as modifying, adding, deleting, and so on. mutation usually stores some methods to modify the state synchronously

Note: the only way to change the state in Vuex's store is to submit the mutation.

How to use mutation

The changes method has a default formal parameter: Change ([state] [, payload])

  • State the state in the current VueX object
  • Payload payload (the parameter passed when the method is called)
//state.js
export default {
  name:'Han Zhiwei'
};
//mutations.js
export default {
 changeName(state, name) {
    state.name = name;
  },
};

We need to call mutation like this

this.$store.commit('changeName','Wu Yanzu')

For example, we modify the name attribute in the methods of the component

methods: {
    changeName(name){
      this.$store.commit('changeName',name);
    },
}
//Call the changeName method
mounted(){
  this.changeName('Wu Yanzu')
}

When multiple parameter submissions are required, they can be placed in an object

this.$store.commit('changeName',{firstName:'han',lastName:'zhiwei'})

You can also pass parameters in another way

this.$store.commit({
    type:'changeName',
    payload:{
        firstName:'han',
        lastName:'zhiwei'
    }
})

What is mapMutation

The mapMutation auxiliary function simply maps the mutation in the store to the component methods

Usage: first import this auxiliary function

import { mapMutation} from 'vuex'

Then you can use mapMutation in methods

methods:{
 ...mapMutations({
      changeName:'changeName',
    })
}

This code is equivalent to the following paragraph

changeName(payLoad){
  this.$store.commit('changeName',payLoad)
}

If the method name is the same as the mutation name, it can be abbreviated as follows

methods:{
 ...mapMutations(['changeName'])
}

You can also use constants instead of the mutations event type

Under the store folder, create a new mutation types JS file

//mutation-types.js
export const ADD_AGE = 'ADD_AGE'
//mutations.js
import * as types from './mutation-types';
export default {
  [types.ADD_AGE](state, payLoad) {
    state.age += payLoad.number
  }
}
//js part of component
 ...mapMutations([types.ADD_AGE]),

But this is not very commonly used. Just know this knowledge point

Add or delete members in state

Now that we have talked about how to modify the value of state, by the way, how to add or delete members in state

Vue.set sets the value of a member for an object. If it does not exist, it will be added

Vue.set(state,"age",22)

Vue.delete delete member

Vue.delete(state,'age')

actions and mapActions

What is actions

The asynchronous operation directly in the mutation method may cause data invalidation. Therefore, Actions is provided for asynchronous operations, similar to axios requests. Finally, the value in state is modified by submitting the mutation method.

How to use actions

The method in Actions has two default parameters: action ([context] [, payload])

  • The context object contains dispatch commit state getters rootState. You can use the deconstruction assignment of es6 to make it more explicit {commit}
  • Payload payload (the parameter passed when the method is called)
    Take an example. After one second, submit mutation to modify the name attribute in state
//state.js
export default {
  name:'Han Zhiwei'
};
//mutations.js
export default {
 changeName(state, name) {
    state.name = name;
  },
};  
//actions.js
export default {
 asyncChangeName({ commit } ,name) {
   setTimeout(() => {
     commit('changeName',name);
  }, 1000);
  },
};

We need to call action like this

this.$store.dispatch('asyncChangeName','Wu Yanzu')

For example, we modify the name attribute in the methods of the component

methods: {
    changeName(name){
      this.$store.dispatch('asyncChangeName',name);
    },
}
//Call the changeName method
mounted(){
  this.changeName('Wu Yanzu')
}

Another action can also be called in action

//actions.js
export default {
 asyncChangeName({ dispatch }) {
   setTimeout(() => {
     dispatch('anotherAction');
  }, 1000);
  },
 anotherAction(){
   console.log('the other one action Called')
 }
};

You can also pass in state and rootState in action. As for what is rootState, you will know when you learn modular modules

//actions.js
export default {
 action({ state }) {
   setTimeout(() => {
      console.log(state.name)
  }, 1000);
  },
 anotherAction({ rootState }){
   setTimeout(() => {
     console.log(rootState.name);
  }, 1000);
 }
};

The parameters of actions are the same as that of mutation

this.$store.dispatch('changeName',{firstName:'han',lastName:'zhiwei'})

What is mapActions

The mapActions helper function simply maps the actions in the store to the component methods

Usage: first import this auxiliary function

import { mapActions} from 'vuex'

Then you can use mapActions in methods

methods:{
 ...mapActions({
      changeName:'changeName',
    })
}

This code is equivalent to the following paragraph

changeName(payLoad){
  this.$store.dispatch('changeName',payLoad)
}

If the method name is the same as the actions name, it can be abbreviated as follows

methods:{
 ...mapActions(['changeName'])
}

modules modularization

What are modules

When the project is huge and has many states, the modular management mode can be adopted. Vuex allows us to split the store into modules. Each module has its own state, mutation, action and getter.

Initialize modules

Previously, we learned how to index vuex JS file is split into a single file for management, so we still manage all modules in a single file split. The directory structure is as follows

store
│      
├─index.js
│            
├─state.js
│      
├─getters.js     
│
├─mutations.js      
│
├─actions.js        
│
└─modules
      │
      ├─moduleA // The structure of moduleA is the same as that of moduleB
      │
      └─moduleB
            │ 
            ├─index.js
            │            
            ├─state.js
            │      
            ├─getters.js     
            │
            ├─mutations.js      
            │
            └─actions.js

1. First root index JS not only introduces its own state, getters, variations and actions, but also introduces the index of two modules JS and export modules in export

import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import getters from './getters';
import mutations from './mutations';
import actions from './actions';
import moduleA  from './modules/moduleA/index';
import moduleB  from './modules/moduleB/index';
Vue.use(Vuex);
export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  modules: {
    moduleA,
    moduleB,
  },
});

2. In the index of module a JS to import the state, getters, variations and actions of moduleA Moduleb is the same

Note: getter, mutation and action are registered in the global namespace by default, so we can use them by default as using the root state, which will lose the meaning of modularization, so we need to use them in the index of the module JS add named: true to make it a module with namespace. When a module is registered, all its getters, actions and mutations will be automatically named according to the path of module registration.

import state from './state';
import getters from './getters';
import mutations from './mutations';
import actions from './actions';
const moduleA = {
  namespaced: true,
  state: state,
  getters: getters,
  mutations: mutations,
  actions: actions,
};
export default moduleA ;

3. The state, getters, variations and actions under module a are the same as those learned before. Just export them

//state.js
export default {
  name:'hzw'
};
//mutations.js
export default {
 changeName(state, name) {
    state.name = name;
  },
};  
//and so on

How to define in modularity

state

Write normally in their respective state JS

getter

getter will have three parameters, the first is the state in the module, the second is getters in the module, and the third is the root node state rootState

//getters.js
export default {
  nameAndAge(state, getters, rootState) {
    return "Age:" + state.age +";"+ getters.realName + "" + rootState.name
  }
};

mutation

The first parameter passed in by mutation is also the state in the module, which is actually the same as when the root state defines mutation

export default {
//The state here is the local state of the module
 changeName(state, name) {
    state.name = name;
  },
};

actions

For action, the context object is only passed in. The state attribute in this object refers to the state in the module, and the rootState refers to the root state, as shown below

export default {
 changeName({ state,rootState }) {
        console.log(state.name)
        console.log(rootState .name)
    }
};

How to develop in modularity

1. state acquisition

This requires adding a module name before the original state name to get the objects in the module.

this.$store.state.moduleA.name;

The same is true for auxiliary functions. Name is preceded by a module name

...mapState({     
  name: state => state.moduleA.name, 
})
//Abbreviation
...mapState('moduleA',['name']),

Getting the status of the root node is the same as before. You do not need to add module name or root

...mapState(['name']),

2. Get Getters

This also needs to add a module name before the original state name to get the objects in the module.

getters in the root state do not need to add a module name

store.getters.moduleA.realName
//The first parameter of the map function also needs to be added with the module name
computed: {
  //Get getters under moduleA
  ...mapGetters("moduleA",["realName","nameAndAge"])
  //Get getters in root state
  ...mapGetters(["realName"])
},

3. Call mutation and action

According to the calculation of state and getters, the module name must also be added when calling the mutation and action in the module

You do not need to add a module name when calling the mutation and action in the root state

methods:{
//Call the action under module A
 ...mapActions('moduleA',['changeName'])
//Call the mutation under module A
 ...mapMutation('moduleB',['changeName'])
//Call action in root state
 ...mapActions(['changeName'])
//Call mutation in root state
 ...mapMutation(['changeName'])
}

4. It should be noted that {root:true} should be passed as the third parameter when the action in the module calls the action and mutation in the root state

//Actions under moduleA js
export default {
 AsyncChangeName({ commit } ,name) {
   setTimeout(() => {
     //It calls the mutation in the root state
     commit('changeName',name,{ root: true });
     //The action in the root state is called
    dispatch('changeName',name,{ root: true });
    }, 1000);
  },
};

5. Register the action in the module as global

This feeling conflicts with the modular design and is not commonly used. Just know this knowledge. When declaring an action, add root:true and put the definition of the action into the hanler function, as follows:

//actions.js
export default {
 globalAction:{
  root:true,
  handler({ commit } ,name) {
   setTimeout(() => {
     commit('changeName',name);
   }, 1000);
  },
 }
};

At this point, you can use vuex for development tasks!

Make an advertisement


This is my project of an open source collection website

Project address 👉👉 Click to enter , it can be directly set as the browser home page or desktop shortcut for use. I am using it and maintain it for a long time.

Completely open source, you can research and secondary development at will. Of course, you are welcome to order a Star ⭐⭐⭐
👉👉Source code link (gitee)       👉👉Source code link (github)

Link integration

🔊 Project preview address (GitHub Pages): 👉👉 https://alanhzw.github.io

🔊 Project preview alternate address (own server): 👉👉 http://warbler.duwanyu.com

🔊 Source address (gitee): 👉👉 https://gitee.com/hzw_0174/warbler-homepage

🔊 Source code address (github): 👉👉 https://github.com/alanhzw/WarblerHomepage

🔊 My blog: 👉👉 https://www.duwanyu.com

Topics: Vue Vue.js