What does Vuex do?

Official explanation: Vuex is a state management mode developed specifically for vue.js applications.

  • It uses centralized storage to manage the state of all components of an application and rules to ensure that the state changes in a predictable way.
  • Vuex is also integrated into Vue’s official debugging tool, DevTools Extension, which provides advanced debugging functions such as zero-configuration time-travel debugging, state snapshot import and export, and so on.

What exactly is state management?

  • State management mode, centralized storage management these terms sound very lofty and confusing.
  • In fact, you can simply think of it as storing all the variables that need to be shared by multiple components in one object.
  • This object is then placed in the top-level Vue instance and made available to other components.

Why Vuex? If you frequently use component arguments to synchronize data values in a project, managing and maintaining these values can be tricky once the project becomes large. My personal understanding is that the biggest use of Vuex is that if you want to keep some common variables on different pages consistent, you can use Vuex to put these variables in a container (Store) and manage them as a big manager, for example:

  • User login status (token), user information (avatar, name, geographic location information)
  • Collection of goods, shopping cart of goods

And these states should be responsive.

Single page data graph

  • State, the data source that drives the application (such as properties in data);
  • View, which declaratively maps state to the view (displaying different information for state changes);
  • Actions, which respond to state changes caused by user input on the view (individual user actions, such as click events).

However, the simplicity of one-way data flow can easily be broken when our application encounters multiple component shared state:

  • Multiple views depend on the same state.
  • Actions from different views need to change the same state.
  • So we need vuEX’s canonical operation to manage state.

Multi-interface state management

Vue already manages the state of a single interface, but what about multiple interfaces?

  • Multiple attempts to rely on the same state (one state changed, multiple interfaces need to be updated)
  • Actions from different screens want to change the same state (home.vue needs to change the state, profile.vue needs to change the state)

That is, some states (state 1/ State 2/ State 3) belong to only one of our attempts, but there are also states (state A/State B/state C) that multiple views want to maintain together.

  • State 1/ State 2/ state you put it in your own room, you manage it for yourself, no problem.
  • But state A/state B/State C we want to be managed by a big steward.
  • Vuex is the tool that gives us this great steward.

The basic use

store/index.js

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {},
  actions: {},
  modules: {}
})
Copy the code

Home.vue

<template> <div class="home"> <h2>{{ $store.state.count }}</h2> <button @click="$store.state.count++">+</button> <button  @click="$store.state.count--">-</button> </div> </template>Copy the code



Add attributes to state in store/index.js, then call and modify them in home.vue. Testing shows that it is possible to modify the vuex state, but in practice it is not recommended to use $store.state.count++ to manipulate vuex state directly.

Vue status management legend

  • Vue Components are Vue Components
  • Mutations: The only way to change the state in Vuex’s store is to submit mutation
  • State is the set of states in VUEX
  • Actions and Mutations are similar and often interact with the back end. The differences lie in:
    • The Action commits mutation rather than a direct state change.
    • Actions can contain any asynchronous operation.

As you can see from the state diagram, if you use $store.state.count++, you are modifying state directly through the vue component. But the official advice was that we need to release an Action first, then submit it to Mutations, and finally revise State. Why is that? Because VUE has a browser plug-in called Devtools, through this plug-in, you can clearly track state changes. If you do not follow the order given by the official, the plug-in cannot detect state changes, resulting in very difficult maintenance and debugging in the later period. Devtools can only detect the state changes of synchronous operations, so asynchronous operations are carried out in Actions and synchronous operations are carried out in Mutations in development.

The use of Mutations

store/index.js

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    }
  },
  actions: {},
  modules: {}
})
Copy the code

Home.vue

<template> <div class="home"> <h2>{{ $store.state.count }}</h2> <button @click="addition">+</button> <button @click="substraction">-</button> </div> </template> <script> export default { name: "Home", data() { return {}; }, methods: { addition() { this.$store.commit("increment"); }, substraction() { this.$store.commit("decrement"); ,}}}; </script>Copy the code

The Devtools plug-in can monitor state changes as they occur.

Passing parameters

When updating data with Mutation, it is possible that we want to carry additional parameters, which are called the Payload of Mutation.

store/index.js

Add5count (state, payload) {state.count += payload}Copy the code

Home.vue

<button @click="add5count(5)">+5</button>

add5count(count) {
      this.$store.commit("add5count", count);
    },
Copy the code

If you want to pass multiple arguments, just pass an object.

store/index.js

addOneStu(state, payload) {
      state.students.push(payload)
    }
Copy the code

Home.vue

<button @click="addOneStu">add</button>

addOneStu() {
      const stu = { id: 16, name: "hello7", age: 23 };
      this.$store.commit("addOneStu", stu);
    },
Copy the code

Mutations submission Style

While doorknocking via commit is a common approach, Vue offers another style, which is an object containing a type attribute.

$code.store.com MIT (" add5Count ", count); Use new code.store.mit ({type: "add5Count ", count,}); },Copy the code
add5count(state, count) {
      console.log(count)
    },
Copy the code

When the Mutations function accepts parameters, the two styles of count have different meanings. The output

Plain commit encapsulation

add5count(state, count) {
      console.log(count)
      state.count += count
    },
Copy the code

Special commit encapsulation

When using special commit encapsulation, the argument is not count itself, but payload, so to retrieve the actual count, you need paypay.count.

add5count(state, payload) {
      console.log(payload)
      state.count += payload.count
    },
Copy the code

Mutations type constant

There are so many mutation methods in a vue file that it is often possible to write them incorrectly, so you can define a constant for mutation type.js in the Store folder.

  1. Define a constant
export const INCREMENT = 'increment'
Copy the code
  1. Import and use where needed
import {
  INCREMENT
} from './mutations-types'

[INCREMENT](state) {
   state.count++
},
Copy the code

Increment does not register an error when using uppercase INCREMENT even if the lowercase ‘increment’ is incorrectly written after defining a constant.

State Understanding of a single State tree

The concept of a single state tree is officially proposed, with a single object containing all the app-level state, which exists as a “unique data source”, meaning that each app will contain only one store instance. A single state tree allows us to directly locate any specific state fragment, easily obtain a snapshot of the entire current application state during debugging, and facilitate management and maintenance during subsequent maintenance and debugging. If state information is stored in multiple Store objects, then it becomes particularly difficult to manage, maintain, and so on.

Getters is basically used

Getters is the calculated property of store, and processing of state is derived data. As with computed properties, the value returned by the getter is cached based on its dependencies and recalculated only if its dependencies change.

store/index.js

export default new Vuex.Store({
  state: {
    count: 0
    students: [{
        id: 10,
        name: 'hello1',
        age: 18
      },
      {
        id: 11,
        name: 'hello2',
        age: 20
      },
      {
        id: 12,
        name: 'hello3',
        age: 15
      },
      {
        id: 13,
        name: 'hello4',
        age: 19
      },
      {
        id: 14,
        name: 'hello5',
        age: 21
      },
      {
        id: 15,
        name: 'hello6',
        age: 25
      },
    ]
  },
  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    }
  },
  getters: {
    powerCounter(state) {
      return state.count * state.count
    },
    more20stu(state) {
      return state.students.filter(s => s.age > 20)
    }
  },
  actions: {},
  modules: {}
})
Copy the code

Home.vue

<template> <div class="home"> <h2>{{ $store.state.count }}</h2> <button @click="addition">+</button> <button @click="substraction">-</button> <h2>{{ $store.getters.powerCounter }}</h2> <h2>{{ $store.getters.more20stu }}</h2> </div> </template> <script> export default { name: "Home", data() { return {}; }, methods: { addition() { this.$store.commit("increment"); }, substraction() { this.$store.commit("decrement"); ,}}}; </script>Copy the code

Call other functions in getters in getters

For example, using the example above to count the number of students over 20, define another function directly in Gettters that takes two arguments.

more20stuLength(state, getters) {
      return getters.more20stu.length
    }
Copy the code

And then call it where you want to use it.

Now we have another requirement, and now we want to be able to pass an argument directly to the function in getters

Getters cannot pass parameters by default, so if you want to pass parameters, you have to make getters return another function.

moreAgestu(state) {
      return function (age) {
        return state.students.filter(s => s.age > age)
      }
    }
Copy the code

Vuex’s responsivity principle

  1. The state of the Vue store is responsive, and the Vue component updates automatically when data in the state changes.
  2. Rules to be followed for responsiveness
    • The state object needs to be initialized
    • If you need to add new attributes to objects in state, use the following methods: a. Using the Vue. Set b (obj, ‘newProp, 123). Replace old objects with new ones
  3. Properties in state are added to the responsive system, which listens for changes in the property and notifies all interfaces where the property is used for a refresh.
  4. Add an object user to state
User: {name: 'wecle', number: 123456, sex: 'male'},Copy the code
  1. Add buttons and methods to home.vue to modify the data
<h2>{{ $store.state.user }}</h2>
<button @click="updateInfo()">update</button>

updateInfo() {
  this.$store.commit("updateInfo");
},
Copy the code
  1. Add methods to store
updateInfo(state) {
  state.user.number = 2333
}
Copy the code
  1. Click Update, the interface refreshes, and Devtools monitors the changes

8. But when we want to add a new attribute directly, such as an address

updateInfo(state) {
  state.user['address'] = 'China'
  // state.user.number = 2333
}
Copy the code
  1. You can see that although Devtools monitors the change, the interface does not refresh

10. To be reactive, use the vue.set () method

updateInfo(state) {
  // state.user['address'] = 'China'
  Vue.set(state.user, 'address', 'China')
}
Copy the code
  1. Click Update again and you’ll see that it’s already responsive

The use of the Action

Action is similar to Mutation, but is used to replace Mutation for asynchronous operations.

  1. Add an aUpdateInfo function to Actions that simulates asynchronous operations with timers
aUpdateInfo(context) {
  setTimeout(() => {
    context.commit('updateInfo')
  }, 1000)
}
Copy the code

Asynchronously calling the Mutation method in a function for synchronous operations, which Devtools can monitor.

  1. Call the aUpdateInfo function in home.vue
updateInfo() {
  this.$store.dispatch("aUpdateInfo");
},
Copy the code

Call the Actions method with this.$store.dispatch.

  1. Click the Update test, and Devtools successfully tracks state changes

  1. Methods in Actions can also pass parameters, just add a payload as a variable.

  2. Actions callback. After an asynchronous operation, there should be a callback for either success or failure. Return a Promise object in the actions method.

aUpdateInfo(context) { return new Promise((resolve, Reject) => {setTimeout(() => {context.com MIT ('updateInfo') resolve(" event has completed ")}, 1000)})}Copy the code
  1. Modify theaUpdateInfo()Method to get the value of the callback.
UpdateInfo () {this.$store.dispatch("aUpdateInfo").then((res) => {console.log(" Commit completed "); console.log(res); }); }Copy the code
  1. Click on theupdate, print the result

The use of Modules

  • Module means Module. Why do we use modules in Vuex?
    • Vue uses a single state tree, which means that a lot of state is handed over to Vuex to manage.
    • 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 of which has a direct state, mutation, action, getters, etc.

The basic use

const moduleA = {
  state: {
    name: "Wecle_2"
  },
  mutations: {
    UpdateName(state, payload) {
      state.name = payload
    }
  },
  actions: {},
  getters: {}
}

const moduleB = {
  state: {},
  mutations: {},
  actions: {},
  getters: {}
}

export default new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
Copy the code

It’s also easy to call

<h2>{{ $store.state.a.name }}</h2>
Copy the code

You can print the results directly

Local state of a module

  1. Mutation and getters inside the module, the first argument received is the module’s local state object.
  2. Action within the module, the local state iscontext.state, the root node status iscontext.rootState.
  3. For the getter inside the module, the third argument is the root node state.
const moduleA = { state: () => ({ count: 0 }), mutations: {increment (state) {// Where the 'state' object is the local state of the module state.count++}}, actions: { incrementIfOddOnRootSum (context) { if ((context.state.count + context.rootState.count) % 2 === 1) { Context.com MIT ('increment') // this commit will only call mutations}}}, getters: {// Methods in modules have an extra rootState parameter, Access to root state doubleCount (state, getters, RootState) {console.log(rootstate.count) return state.count * 2}}}Copy the code

The action of the context

We could write it like this

actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
        if ((context.state.count + context.rootState.count) % 2 === 1) {
            context.commit('increment')
        }
    }
}
Copy the code

This belongs to the deconstruction of objects in ES6.

Store File directory organization

Vuex doesn’t limit your code structure. However, it lays down some rules that need to be followed:

  1. Application-level state should be centralized into a single Store object.
  2. Submitting mutation is the only way to change the state, and the process is synchronous.
  3. Asynchronous logic should be wrapped in actions.

As long as you follow these rules, you can organize your code any way you want. If your store file is too large, just split the action, mutation, and getter into separate files.

For large applications, we would want to split Vuex related code into modules. Here is an example project structure: