This is the 10th day of my participation in the More text Challenge. For more details, see more text Challenge

introduce

Vuex is a state management pattern 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 manner. Official document.

Note: Not all items require Vuex, you only need to use Vuex if vue has no way to solve it or if you need to store data fields that are considered global variables in vue applications. Abuse of Vuex can lead to problems such as global data pollution that is not conducive to maintenance.

VueX configuration

// File path/SRC /store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)  // Register the configuration prototype method provided by vuex with Vue

export default new Vuex.Store({ // Instantiate the vuex. Store object and expose it
    state: {
        value'Global state in the Vuex Store'}})Copy the code
// main.js Vue application entry file Configures the Store to the Vue instance object

import Vue from 'vue'
import store from './store' // Introduce the Vuex Store object
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  store, // Add the vuex store to the vue instance
  renderh= > h(App),
}).$mount('#app')
Copy the code

The core concept of Vuex

  • State

  • Getters

  • Mutations

  • Actions

  • Modules

Warm prompt

Import {mapState, mapGetters, mapActions} from 'vuex'; import {mapState, mapGetters, mapMutations, mapActions} from 'vuex';Copy the code

State

The concept is that Vuex is a place to store data, which is stored in state as a global variable of the current application and can be accessed anywhere in the current application.

// src/store/index.js

export default new Vuex.Store({
  state: {
    book: 'HTML+CSS',}})Copy the code

grammar

  1. You can use this.$store.state in any component of vUE. Attribute name access
let store = new Vuex.Store({
    state: {
        value'Global status value in the VUex Store'}})let vm = new Vue({
    el: '#app',
    store 
})
vm.$store.state.value // 'global status value in the vuex Store'
Copy the code
  1. In development, it is recommended to assign the state in the store to the computed property of the component that needs to use the state (never assign state to data, and do not re-assign data when state changes).
export default {
    data() {
        return {
            // The error state. Book is not updated when the error state
            myBookthis.$store.state.book 
        }
    },
    computed: {
        book() {
             // The correct state.book will be used as a dependency for the current computed property, and the computed property book will recalculate the current value when state.book changes
            return this.$store.state.book
        }
    }
}
Copy the code
  1. Vuex provides a helper function to simplify the code when state is used with computed propertiesmapStateWe can simplify this
<template>
    <div>{{book}}</div>
</template>

<script>
// First introduce the helper function mapState
import {mapState} from 'vuex'

export default {
    /* computed: { book() { return this.$store.state.book; }} * /
    
    // The following is equivalent to the above
    computed: {
        ...mapState(['book'])}}</script>
Copy the code

Getters

A conceptual getter is a Vuex computed property. Developers can store the computed return value of state or other getters in the specified getter. The current getter caches these dependencies and will only be recalculated if its dependency values change

This.$store.getter can be used in any component. Attribute name access

// src/store/index.js

export default new Vuex.Store({
  state: {
    score: [30.20.80.10.9.66],},getters: {
    The return value of a getter is cached according to its dependencies, and is only recalculated if its dependencies change.
    calcScore(state) {
      // p(state, getters, rootState, rootGetters)
      //state: indicates the state of the current module
      //getters: getters for the current module
      //rootState: indicates the global state
      //rootGetters: global getters
      let ps = state.score.filter(v= > {
        return v >= 30;
      })
      return ps.join(The '-'); }}})Copy the code

A syntactic getter takes a function as an argument that has multiple internal arguments and returns the value of the current getter

  • Parameter 1 state Indicates all statesCopy the code
  • Parameter 2 getters all gettersCopy the code
  1. Getters remain in the component in the component’s computed properties
<template>
    <div>{{score}}</div>
</template>

<script>
export default {
    computed: {
        score() {
          return this.$store.getters.calcScore; }}}</script>
Copy the code
  1. Vuex also provides helper functions for gettersmapGetters
  • MapState accepts an array of strings as arguments. Each string in the array becomes a computed property of the current component and maps to the getters of the same name in Vuex.
<template>
    <div>{{calcScore}}</div>
</template>

<script>
// First introduce the helper function mapGetters
import {mapGetters} from 'vuex'

export default {
    /* computed: { book() { return this.$store.getters.calcScore; }} * /
    
    // The following is equivalent to the above
    computed: {
       ...mapGetters(["calcScore"])}}</script>
Copy the code
  • MapGetters can take an object as an argument. Each key of the object becomes the name of the computed property of the current component. Value must be a string that maps to the getters of the same name in Vuex.
<template>
   <div>{{s1}}</div>
</template>

<script>
import {mapGetters} from 'vuex'
export default {
    computed: {
        ...mapGetters({
            s1'calcScore'}}})</script>
Copy the code

Mutations

The concept VUex states that mutation is the only place where state can be modified

export default new Vuex.Store({
  state: {
    book: 'HTML+CSS',},mutations: { // Modify the state method
    // Modify the synchronization operation directly
    changeBook(state, book){ state.book = book; }},})Copy the code

The syntax is created in VUex by configuration option Mutations and the mutation is submitted using the $store.com MIT method

1. The mutation must be submitted in the component using the $store.com MIT method before the mutation is triggered

<template>
    <div>
        <button @click="changeBookByCom">Mutations: I change the books.</button>
    </div>
</template>
<script>
export default {
    methods: {
        // Executing this method will change the book in state to JavaScript instead of the original HTML+CSS
        changeBookByCommit() {
            To trigger an mutation, the mutation must be committed once using the commit method provided by $store
            // The commit method receives two parameters, one is the string parameter with the same name and the other is the payload that needs to trigger the mutation
            this.$store.commit("changeBook"."JavaScript");
        }
    }
}
</script
Copy the code

Note: Commit can only take two arguments. If you want to pass more than one argument, commit the payload as an object

Store.com MIT the only method to submit mutation, which takes at most two arguments: Parameter 1 Mutation Type <String> Mandatory Name of the mutation function to be triggered parameter 2 Payload <Any> Optional parameter to be submitted to the mutationCopy the code
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        count1.arr: ['a'.'b'.'c'],},mutations: {
        addCurrentCount(state, num) {
            state.count += num
        },
        setArr(state, {index, value}) {
           Vue.set(state.arr, index , value)
        }
    }
})
Copy the code
<template>
    <div>
        <button @click="setStateCount">add count</button>
        <button @click="setStateArr">setStateArr</button>
    </div>
</template>
<script>
export default {
    methods: {
        setStateCount() {
            this.$store.commit('addCurrentCount'.7)},setStateArr() {
            // Here the payload is an object
            this.$store.commit('setArr', {index:0.value: 7})}}}</script>
Copy the code

The status in the Store of Vuex is responsive, so when we change the status, the Vue component that monitors the status also updates automatically. This also means that mutation in Vuex is subject to the same precautions as when using Vue:

1. It is best to initialize all the required state properties in your store in advance.

2. When you need to add new properties to an object, you should either use vue.set (obj, ‘newProp’, 123), or replace the old object with a new one

Vue.set(obj, ‘newProp’, 123)

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        count1.arr: ['a'.'b'.'c'].obj: {
            name'xiaohua'.age15}},mutations: {
        addCount(state) {
            state.count ++
        },
        setArr(state) {
            // Vuex still follows the responsive rule. In vue's responsive rule, adding an item to an array by subscript changes does not cause the page to update
            // state.arr[0] = 'A'
            // vuex does not have this, so you must import Vue first, and then modify the array using vue.set
            Vue.set(state.arr, 0 , 'A')},setObj(state) {
            // state.obj.address = 'hz' // Error Adding properties to an object via key will not cause page updates
            Vue.set(state.obj,'address'.'gz')}}})Copy the code

New object replaces old object

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        arr: ['a'.'b'.'c'].obj: {
            name'xiaohua'.age15}},mutations: {
        addCount(state) {
            state.count ++
        },
        setArr(state) {
            // Vuex still follows the responsive rule. In vue's responsive rule, adding an item to an array by subscript changes does not cause the page to update
            // state.arr[0] = 'A'
            // The method of replacing the old object with a new object causes the reference data type to respond to the change
            state.arr = state.arr.map((item,index) = > {
                if(index === 0) {
                    return 'AA'
                }
                return item
            })
        },
        setObj(state) {
            // state.obj.address = 'hz' // Error Adding properties to an object via key will not cause page updatesstate.obj = { ... state.obj,address'guangzhou'}}}})Copy the code

Note: Mutation Internally, only synchronous operations are allowed, because mutation is the only place in VUex where state can be modified, so in development we sometimes do data tracking on mutation to see if state changes as expected, and this allows a large number of errors to be caught in development if mutation allows asynchronous operations To do so would result in our data tracking becoming confusing and inaccessible.

Note: Since every commit mutation is a mutation string with the same name that is passed into the COMMIT function, this operation can sometimes cause the commit string to be inconsistent with the mutation name and cause the specified mutation not to be committed, especially in multiplayer development. To avoid this problem, VUex recommends creating a file called mutation-types. Js that stores all the mutations in the current application, allowing developers to declare and submit the specified mutations as variables

grammar

/ / path/store/mutation - types. Js
// Mutation names are always in constant form. The general rule is that constants are in all uppercase form and each word is separated by an underscore
// Each mutation needs a note explaining what it was for
export const ADD_CURRENT_COUNT = 'ADD_CURRENT_COUNT'

export const SET_ARR = 'SET_ARR'

export const SET_OBJ = 'SET_OBJ'
Copy the code
// All the mutations in store use constants declared by mutation-types as function names
import Vue from 'vue'
import Vuex from 'vuex'
import * as types from './mutation-types'

Vue.use(Vuex)

export default new Vuex.Store({
    mutations: {
        // ES6 dynamic key names take the constants declared in mutation-types as the function names of the current mutation
        [types.ADD_CURRENT_COUNT](state, num) {
               // some code...
        },
        [types.SET_ARR](state, {index, value}) {
              // some code...
        },
        [types.SET_OBJ](state) {
            // some code...}}})Copy the code
// Components call these mutations directly by introducing the corresponding mutation-types constant
<script>
import {ADD_CURRENT_COUNT, SET_ARR, SET_OBJ} from '.. /store/mutation-types'

export default {
    methods: {
        setStateCount() {
            this.$store.commit(ADD_CURRENT_COUNT, 7)},setStateArr() {
            this.$store.commit(SET_ARR)
        },
        setStateObj() {
            this.$store.commit(SET_OBJ)
        }
    }
}
</script>
Copy the code

2. Vuex also provides an auxiliary function, mapMutations, for mutations

MapMutations generation accepts only one parameter, which is the current mutation load.

Method 1: mapMutations can receive an array of strings as a parameter, and each string item in the array will become the method of the current component and establish a mapping mapping relationship with the same mutation in Vuex.

<template>
    <div>// Submit load<button @click="ADD_CURRENT_COUNT(3)">add count</button>// Do not submit load<button @click="SET_ARR">set arr</button>
    </div>
</template>
<script>
import {mapMutations} from 'vuex'
import {ADD_CURRENT_COUNT, SET_ARR} from '.. /store/mutation-types'

export default {
    methods: {
        ...mapMutations([ ADD_CURRENT_COUNT, SET_ARR ]),
    }
}
</script>
Copy the code

Method 2: mapMutations can receive the object as a parameter, and each key of the object will become the method name of the current component, and value must be the string with the same name of mutation to establish the mapping correspondence with mutation in Vuex.

<template>
    <div>// Submit load<button @click="addCount(3)">add count</button>// Do not submit load<button @click="setArr">set arr</button>
    </div>
</template>
<script>
import {mapMutations} from 'vuex'
import {ADD_CURRENT_COUNT, SET_ARR} from '.. /store/mutation-types'

export default {
    methods: {
        ...mapMutations({ 
         addCount: ADD_CURRENT_COUNT, 
         setArr: SET_ARR }),
    }
}
</script>
Copy the code

Actions

The Vuex concept provides developers with a function, action, that can perform asynchronous operations

Note: The action function takes two arguments

Argument 1 Context is similar to the store object so you can access context.state/context.getters/context.com MIT/context.dispatch

The action payload is the same as the mutation. If you need to pass more than one parameter, pass the object

★★★ Action Is not allowed to change the state directly

The syntax is created in Vuex by configuring the option Actions, and the action is distributed using the $store.dispatch method

1. Called in the application as store.dispatch(‘action name ‘, payload)

import Vue from 'vue'
import Vuex from 'vuex'
import * as types from './mutation-types'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        result: {}},mutations: {
        [types.SET_RESULT](state, result) {
            / / modify state
            state.result = result
        }
    },
    actions: {
        searchMusic(context, keywords) {
            console.log(context)
            fetch('http://musicapi.leanapp.cn/search?keywords=' + keywords)
            .then(res= > res.json())
            // The state can be changed only by mutation. Therefore, after action asynchronously requests data, the state can be changed only by submitting mutation
            .then(({result}) = > context.commit(types.SET_RESULT, result))
        }
    }
})
Copy the code
<script>
export default {
  created() {
      this.$store.dispatch('searchMusic'.'Sea and sky')}}; </script>Copy the code

Vuex also provides a helper function called mapActions for actions

The method generated by mapActions takes only one parameter, which is the payload of the current action.

MapActions takes an array of strings as arguments. Each string in the array becomes the method of the current component and maps to Vuex actions of the same name.

<script>
import {mapActions} from 'vuex'

export default {
  created() {
    // this.$store.dispatch('searchMusic',' searchMusic')
    this.searchMusic('Sea and sky')},methods: mapActions(['searchMusic']),}; </script>Copy the code

Method 2: mapAction can receive objects as parameters. Each key of the object will become the method name of the current component. Value must be the string with the same name as the action in Vuex to establish a mapping relationship.

<script>
import {mapState, mapActions} from 'vuex'

export default {
  created() {
    // this.$store.dispatch('searchMusic',' searchMusic')
    this.search('Sea and sky')},methods: mapActions({search'searchMusic'})}; </script>Copy the code

Modules

Due to the length of the article, the use of modularity in Vuex is covered in the next article.

conclusion

  1. The state of the application hierarchy should be concentrated in the Store

  2. Submitting mutation is the only way to change the state state, and the process is synchronous.

  3. Asynchronous operations should be placed in the action

Series of links

CodeSandbox online code demo

Pay attention toIf CodeSandBox fails to open, please try the following operations, and then the window can be viewed while looking at the code to see the effect

  • Vuex series (I) — the use of Vuex
  • Vuex series (ii) — The use of modularity
  • Vuex Series (III) — the difference and usage of store.com MIT and Store. dispatch
  • Vuex series (IV) — Auxiliary function mapMutations analysis