An overview of the

When developing complex VUE applications, VUEX can help you manage shared state among multiple components.

Using new vuex.store (options), you can build a store instance. The store is the core of the Vuex app. You can access the shared state in the app through store.state, and you can access the new state derived from the shared state through store.getters. The store.com MIT method allows you to submit mutation to change the shared state, and the Store.Dispatch method allows you to dispatch action to commit mutaion asynchronously.

In addition, when the store becomes bloated due to the complexity of the application, we can divide the store into modules, and each module can have its own state, getters, mutations, actions and even nested child modules.

The problem

  1. How is vuex installed? What was done during the installation?

  2. Why does changing the data in state trigger updates?

  3. How does the getter work?

  4. Why not change the state of vuex directly by store.state.xx?

  5. The effects of the namespace on state, getter, mutation, and action

  6. How does strict work?

  7. Use of state, getter, mutation, and action in components

  8. other

Install vuex

Before using vuex to develop vUE applications, you need to install vuex first.

The installation process of vuEX is as follows:

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

Vue.use(Vuex)
Copy the code

When using use to install vuex, vue automatically executes the install method provided by vuex. The installation method of vuex is as follows:

// install

function install (_Vue) {
  if (Vue && _Vue === Vue) {
    {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      );
    }
    return} Vue = _Vue; // Execute the applyMixin method applyMixin(Vue); }Copy the code

In the install method provided by Vuex, the main process is to execute the applyMixin method. The specific process is as follows:

// applyMixin

functionApplyMixin (Vue) {// get the current version of Vue const version = Number(vue.version.split ())'. ') [0]);if(version >= 2) {// use vue2 and above, trigger the beforeCreate hook function when each vUE instance is built, execute vuexInit method vue.mixin ({beforeCreate: vuexInit}); }else{// Override init and inject vuex init procedure //for 1.x backwards compatibility.
    const _init = Vue.prototype._init;
    Vue.prototype._init = function(options = {}) { options.init = options.init ? [vuexInit].concat(options.init) : vuexInit; _init.call(this, options); }; } // When each vue instance is built, the vuexInit method is executedfunction vuexInit() {// this -> current vue instance const options = this.$options;
    // store injection
    if(options.store) {// of the root vue instance$storeAttribute this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store;
    } else if (options.parent && options.parent.$store) {// component vue instance,$storeProperty that points to the root Vue instance$storeAttribute this.$store = options.parent.$store; }}Copy the code

In the applyMixin method, register the beforeCreate hook globally, that is, each vue instance will trigger the beforeCreate hook at build time, executing the vuexInit method.

When executing the vuexInit method, create the $store attribute for the vue instance. Both the root vUE instance and the component vue instance, the $store attribute points to the store instance built with new vuex. store. All Vue instances have the same value of the $store attribute.

Vuex does one thing: register beforeCreate for vue global. When creating a vue instance, trigger beforeCreate to add the $store attribute to the vue instance, pointing to the store instance built with new vuex.store.

How the VUEX – responsive works

When developing vUE applications using Vuex, we define the state and then use the defined state in the component through computed properties. As follows:

var option = {
    state: {
        money: 10000
    },
    mutations: {
        setMoney(state, money) {
            state.money = money
        }
    }
}

var bill = {
    data() {
        return{... } }, computed: {finalMoney() {// The unit of money is in centisreturn parseInt(this.$store.state.money / 1000)
        }
    },
    methods: {
        setDisount() {
            this.$store.commit('setMoney', this.money)
        }
    }
    ...
    
}
Copy the code

In the example above, the billing component uses the vuex state – money when calculating the charges. When the vuex state – money is modified through the component method setMoney, the charges previously calculated by the billing component are also updated.

So how does vuex’s responsive update trigger?

In fact, the responsive principle of VUEX is based on the responsive principle of VUE.

During the construction of vUE instance, if the configuration item contains the data attribute data, each attribute in the data will be deeply traversed, a dePS list will be established for each attribute, and getters and setters will be added for each attribute through defineProperty method. If the data property is used by a template, a computed property, or a listening property, the getter for the data property is triggered, and the watcher is added to the deps list for the data property. When the data property changes, it fires the setter, notifying the Watcher in the deps list of updates, and then rerendering the interface, returning the value of the new computed property, and triggering the listening callback.

Vuex responsive update is implemented when building a store instance with new vuex.store (options). The main process is as follows:

  1. Create the root Module and child modules.

    When building a Store instance, a root Module object is first generated based on the passed configuration options(state, getters, mutation, and so on). If options contains modules and nested modules, then it traverses modules and nested modules, creating the corresponding Module object.

    Each Module object contains its own state, getters, Actons, mutations, and nested submodules.

    The Module object collects its associated child Module objects through the children property (array).

  2. Collect the state in nested child modules into the state of the root Module.

    // options
    var option = {
        state: {
            name: ' '}, modules: {trade: {state: {money: 1000}, modules: {trade: {state: {money: 1000}, modules: {loan: {state: {discount: 0.8}, modules: {confirm: {state: {num: 2}}}}, product: {state: {discount: 0.9}}}}}} { name:' ', trade: {loan: {discount: 0.8, Confirm: {num: 2}}, product: {discount: 0.9}}}Copy the code
  3. Use new Vue to build a Vue instance -_VM for the Store instance.

    When building a VUE instance, the root Module’s state attribute is used as a data attribute.

    store._vm = new Vue({
        data: {
          $$state: state // state is the root Module state after collecting submodule states}});Copy the code

    After the vue instance is built,? Each property in state becomes a responsive property, with getters, setters, and a list of DePs maintained.

    In the vue component, we can use this.$store.state to access the _VM instance? The principle of the state attribute is as follows:

    class Store { ... // Access the state property of the store instance, essentially accessing store._vm._data.$$stateAttribute the getstate () {
            return this._vm._data.$$state}}Copy the code

    If a vuex state is used in the Template, computed, or watcher attribute in this.$store.state mode, the vuEX state is set to _vm. Getters for the property of the same name in data, and the corresponding watcher is added to the deps list for the property of the same name. When changing the vuex state, trigger the VUex state in _vm.? The setter for the property of the same name in data notifying the Watcher in the deps list of updates, then rerendering the interface, returning the value of the new computed property, and triggering the listening callback.

How getters work

The getter, you can think of it as the calculated property of the store. Just like vue’s computed properties, the return value of the getter is cached and only recalculated if the dependent state state changes.

Getters are implemented based on vUE’s computed property.

The implementation principle of vUE computing attributes is as follows:

  1. When creating a VUE instance, you need an Options configuration item, including data, props, computed, and methods. If options has computed properties, you need to walk through the computed properties and create a watcher for each property. In addition, for each computed property, a property of the same name is created for the Vue instance using defineProperty and the getter is set. When a evaluated property is accessed through a Vue instance, a getter is fired to execute the method corresponding to the evaluated property.

  2. For each computed property, there is a watcher. Watcher has a flag property: dirty. When the value of dirty is true, the value of the calculated attribute is obtained. The method corresponding to the calculated attribute needs to be executed and the returned result is cached. When dirty is false, the cache value is returned.

    When watcher is first built, the dirty value defaults to true.

    After the method that evaluates the property is executed, the value of dirty is set to false.

  3. The first time you use the computed property, you need to use the method corresponding to the computed property because the watcher.dirty value is true. When the method is executed, it reads the value of the responsive property (data, props) and fires the getter method for the responsive property. The Watcher who evaluates the property is added to the list of DEPs for responsive properties. In addition, the value of watcher.dirty is set to false.

  4. If the dependent responsive property changes, the setter method is fired to notify the watcher in the DEps list of updates. The watcher.dirty value is set to true. The next time a computed property is used, the corresponding method of the computed property is recalculated.

  5. Watcher. dirty will always be false if the dependent responsive property has not changed. The next time a calculated property is used, the cached result of the last calculation is directly returned.

The getter for vuex is also implemented when building the store instance using new vuex.store (options). The main process is as follows:

  1. Create the root Module and child modules.

  2. Collect the state in child modules into the state of the root Module.

  3. Collect getters from the root Module and child modules into the _wrappedGetters object of the Store instance.

  4. The _wrappedGetters object of the store instance is used as the computed configuration item, and the state object of the root Module is used as the data configuration item to build the VUE instance _VM.

    store._vm = new Vue({
        data: {
          $$state: state // State indicates the root module state} after the submodule state is collected, computed: _wrappedGetters}).Copy the code
  5. Add the getters property to the Store instance. Go through the _wrappedGetters attribute and add attributes to store.getters with defineProperty and set the getter method. This allows us to access the store._VM computing property of the same name via store.getters. Xx.

When we access the getters defined by vuex in the component using this.$store.getters. Xx, it is equivalent to accessing the computed property of the same name in this.$store._vm.

When you access the getter of vuex for the first time, the value of watcher.dirty corresponding to the calculation property of the same name is true, and you need to execute the method corresponding to the calculation property. When executed, it reads the value of the dependent state, triggers the getter method for state, and then reads this.$store._vm._data.? The value of the responsive property of the same name in data triggers the getter method for the responsive property. At this point, the watcher with the same name as the computed property is added to the list of DEPs for the responsive property. After the method with the same name is executed, the result is cached and the watcher.dirty value is set to false.

If the state that vuex’s getter depends on changes, this.$store._vm._data.? The setter for the corresponding responder property of the same name in data is fired and the watcher in the DEps list is notified of the update. The watcher.dirty value of the same name of the vuex getter is set to true. The next time vuex’s getter is accessed, the new value is returned, depending on the dependent state.

If the state that vuex’s getter depends on remains unchanged, the watcher.dirty value of the corresponding property with the same name is always false. The next time vuex’s getter is accessed, the result of the computed property cache of the same name is returned.

Strict mode

When building a Store instance using new Vuex. Store, strict mode is enabled if the strict configuration item is true.

With strict mode enabled, an exception is thrown if the state change was not caused by mutation.

If you change state directly and if you change state via mutation, you change state. Why does it throw an exception?

In VUex, there is a section of source code that deals with strict patterns, as follows:

function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.$$state }, () => {
    {
      assert(store._committing, `do not mutate vuex store state outside mutation handlers.`);
    }
  }, { deep: true, sync: true });
}
Copy the code

When we build a Store instance, we build a vue instance for the store instance: _VM. In strict mode, run the enableStrictMode method.

In the enableStrictMode method, is _VM listening? The state. If? If state changes, the callback is executed.

Whether you change the state directly or by mutation, it will result in? The state changes, and then the callback is triggered. The difference is that, with mutation, store._research is true and no exception is thrown, whereas with direct modification, store._research is false and an exception is thrown.

By default, the strict mode is disabled. That is, if the strict configuration item is not set when the Store instance * is built, the strict mode is not enabled.

Note: Do not enable strict mode in a production environment.

Change the state

Vuex suggested that we change state by submitting mutation for the following reasons:

  1. In strict mode (strict: true), an exception is thrown if the state is changed by store.state.xx = XXX.

  2. After dev-tools(devtools: true) is enabled, if the state is changed in store.state.xx = XXX, the state change cannot be tracked by dev-tools.

After dev-tools is enabled, when we change the state by submitting mutation, the change in state can be tracked by dev-tools. In the VUex list of dev-Tools, we can clearly see each commit operation.

When building the store instance, the dev-tools plug-in subscribs to the store mutation via the store.subscribe method, which registers a callback. When store.com MIT (type, payload) is executed, mutations corresponding to type are triggered first, and then callback is triggered to notify dev-tools to track mutation. When the callback is triggered, the mutation and the state after the mutation are passed in as parameters.

Namespace – Namespace

By default, actions, mutations, and getters within a module are registered in the global namespace, enabling multiple modules to respond to the same mutation or action.

If you want more encapsulation and reuse, you can make your module namespaced: true by adding namespaced: true. When a module is registered, all its getters, actions, and mutations are automatically named according to the registered path of the module.

The effects of namespaces on module getters, action, and mutation are shown by an example:

  var option = {
    // root module
    state: { name: 'root' },
    getters: { getName: state => state.name },
    mutations: { setName: (state, name) => { state.name = name }},
    actions: { setName: (context, name) => { context.commit('setName', name) }},
    modules: {
      // module A
      moduleA: {
        namespaced: true,
        state: { name: 'moduleA' },
        getters: { getName: state => state.name },
        mutations: { setName: (state, name) => { state.name = name }},
        actions: { setName: (context, name) => { context.commit('setName', name) }}
        modules: {
          // module C
          moduleC: {
            state: { name: 'moduleC' },
            getters: { getName: state => state.name },
            mutations: { setName: (state, name) => { state.name = name }},
            actions: { setName: (context, name) => { context.commit('setName', name) }}
          },
          // module D
          moduleD: {
            namespaced: true,
            state: { name: 'moduleD' },
            getters: { getName: state => state.name },
            mutations: { setName: (state, name) => { state.name = name }},
            actions: { setName: (context, name) => { context.commit('setName', name) }}
          }
        }
      },
      // module B
      moduleB: {
        state: { name: 'moduleB' },
        getters: { getName: state => state.name },
        mutations: { setName: (state, name) => { state.name = name }},
        actions: { setName: (context, name) => { context.commit('setName', name) }}
      }
    }
  }
Copy the code

In the above example, store is cut into root Module, moduleA, moduleB, moduleC, moduleD.

The root Module, whether namespced is true or false, has an empty namespace, that is, the global namespace.

ModuleA, namespaced is true, and its namespace is ‘moduleA’.

ModuleB, namespaced is not true, and it inherits the namespace of its parent module, the root Module, which makes its namespace the global namespace.

ModuleC, namespaced is not true, and it inherits the moduleA namespace from its parent Module, which is called ‘moduleA’.

ModuleD, Namespaced is true, and its namespace is ‘moduleA/moduleD’.

The namespace affects the use of getters, mutations, and Actions:

  • getter

    When building store instances, getters from each module are collected into store._wrappedgetters.

    Store._wrappedgetters is an object with a property name: namespace /getter name and the corresponding property value is a function – the wrapped getter.

    Since _wrappedGetters is an object, a warning will be thrown if there is a getter with the same property name.

    In the example above, _wrappedGetters, the getters collected are as follows:

        _wrappedGetters = {
            'getName': function() {... }, // the getter in the root Module'moduleA/getName': function() {... }, // getter for moduleA'moduleA/moduleD/getName': function() {... } // moduleD getters}Copy the code

    For moduleB, the namespace is global and the corresponding attribute is named getName. At this time, an attribute with the same name already exists in _wrappedGetters and cannot be added and a warning is thrown.

    The same is true for moduleC, the namespace is moduleA and the corresponding attribute is moduleA/getName, and an attribute with the same name already exists in _wrappedGetters, which cannot be added and a warning is thrown.

    Within the component, we can access getters through store.getters. Xx, which corresponds to the name of the property in _wrappedGetters, as follows:

        this.$store.getters.getName // Access the root Module's getName this.$store.getters['moduleA/getName'] // Access moduleA's getName this.$store.getters['moduleA/moduleD/getName'] // Access the getName of moduleDCopy the code

    GetName for moduleB, moduleC is not collected in _wrappedGetters and cannot be accessed in the component.

  • mutation

    Mutations of each module are collected into store._mutations when building store instances.

    Store._mutations is an object, and the attribute name is: namespace /mutation, and the corresponding attribute value is an array, and the array element is the wrapped mutation.

    In the example above, _mutations, the mutations collected are as follows:

    _mutations: {// fn_root corresponds to the root modulesetName, fnB corresponds to moduleBsetName
            'setName': [fn_root, fnB], // fnA corresponds to moduleAsetName, fnC corresponds to moduleCsetName
            'moduleA/setName': [fnA, fnC], // fnD corresponds to moduleDsetName
            'moduleA/moduleD/setName': [fnD]
        }
    Copy the code

    ModuleB’s namespace is the global namespace, and the corresponding attribute is named setName. When _mutations exists, mutation is added to the array corresponding to setName.

    The namespace of moduleC is moduleA, and the corresponding attribute is named moduleA/setName. When _mutations exists, mutation is added to the array corresponding to moduleA/setName.

    In the component, we can submit mutation in the way of store.com MIT (type, payload), type will correspond to the attribute name in _mutations, as follows:

    // Trigger the root module, moduleBsetName
        this.$store.commit('setName'.'xxx') // Trigger moduleA, moduleCsetName
        this.$store.commit('moduleA/setName'.'xxx') // Trigger moduleDsetName
        this.$store.commit('moduleA/moduleD/setName', xxx)
    Copy the code

    In the above example, the root Module and moduleB have the same namespace, and when ‘setName’ is committed, the mutent-setname in both root Module and moduleB triggers; ModuleA and moduleC have the same namespace, and the mutent-setname in both moduleA and moduleC is triggered when ‘moduleA/setName’ is submitted.

  • actions

    When building a Store instance, the actions for each module are collected into store._actions.

    Store._actions is an object with an attribute name: namespace /action name. The corresponding attribute value is an array whose elements are wrapped actions.

    In the example above, in _actions, the collected actions are as follows:

    _actions: {// fn_root Of the corresponding root modulesetName, fnB corresponds to moduleBsetName
            'setName': [fn_root, fnB], // fnA corresponds to moduleAsetName, fnC corresponds to moduleCsetName
            'moduleA/setName': [fnA, fnC], // fnD corresponds to moduleDsetName
            'moduleA/moduleD/setName': [fnD]
        }
    Copy the code

    ModuleB’s namespace is the global namespace, and the corresponding attribute is named setName, which already exists in _actions. Add the action to the array corresponding to setName.

    The moduleC namespace is moduleA, and the corresponding property is called moduleA/setName, which already exists in _actions. Add the action to the array corresponding to moduleA/setName.

    Within the component, we can dispatch an action by means of store.dispatch(type, payload), which corresponds to the _actions attribute name, as follows:

    // Trigger the root module, moduleBsetName
        this.$store.dispatch('setName'.'xxx') // Trigger moduleA, moduleCsetName
        this.$store.dispatch('moduleA/setName'.'xxx') // Trigger moduleDsetName
        this.$store.dispatch('moduleA/moduleD/setName', xxx)
    Copy the code

    In the example above, the root Module and moduleB have the same namespace, and when ‘setName’ is dispatched, the action – setName in both root Module and moduleB fires; ModuleA and moduleC have the same namespace, and when ‘moduleA/setName’ is dispatched, action-setName in both moduleA and moduleC is triggered.

The use of state

Typically, we will compute properties in the VUE component to access the required state-state.

The specific way, we use an example to illustrate.

var options = {
    state: {
        name: 'zhangsan'
    },
    modules: {
        trade: {
            namespaced: true,
            state: {
                type: 'waiting'
            },
            modules: {
                product: {
                    namespaced: true,
                    state: {
                        id: 1
                    }
                }
            }
        },
        loan: {
            namespaced: true,
            state: {
                money: 100000
            }
        }
    }
}
Copy the code
  • Direct access to the

    We can directly access the required status-state by using vm.$store.state.xx.

    var bill = {
        data() {
            return{... } }, computed: {name() {
                return this.$store.state.name
            },
            tradeType() {
                return this.$store.state.trade.type
            },
            productId() {
                return this.$store.state.trade.product.id  
            },
            loanMoney() {
                return parseInt(this.$store.state.loan.money / 1000)
            }
        }
    }
    Copy the code
  • With the mapState helper function

    We can use the helper function – mapState to help us generate computed properties.

    var mapState = Vuex.mapState
    
    var bill = {
        data() {
            return{... } }, computed: { ... MapState ({// global namespace name:'name', tradeType: state => state.trade.type // global state}),... mpaState('trade/product', {// trade/product namespace productId:'id'
            }),
            ...mapState('loan', {// loan namespace loanMoney: state => parseInt(state.money / 1000) // local state})}}Copy the code
  • Use the createNamespacedHelpers helper function

    We can use helper functions such as createNamespacedHelpers and mapState to help us generate calculation properties.

    CreateNamespacedHelpers can help you generate namespace-based helper functions.

    MapState based on the global namespace var mapState = Vuex. MapState // mapState based on the trade namespace var tradeMapState = Vuex.createNamespacedHelpers('trade'). The mapState / / trade/product namespace mapState var productMapState = Vuex. CreateNamespacedHelpers ('trade/product'). The mapState / / based on loan namespace mapState var loanMapState = Vuex. CreateNamespacedHelpers ('loan').mapState
    
    var bill = {
        data() {
            return{... } }, computed: { ... mapState(['name']), // calculate the property name name and return the value this.$store.state.name ... tradeMapState({ tradeType:'type'// Calculate a property named tradeType and return a value of this.$store.state.trade.type }), ... productMapState({ productId:'id'
            }),
            ...mapState({
                loanMoney: state => parseInt(state.money / 1000)
            })
        }
    }
    Copy the code

The use of getter

The use of the getter for vuex, similar to state, is also illustrated with an example.

var options = {
    state: {
        name: 'zhangsan'
    },
    getters: {
        getName(state) {
            return state.name
        }
    },
    modules: {
        trade: {
            namespaced: true,
            state: {
                type: 'waiting'
            },
            getters: {
              getTradeType(state) {
                  return state.type
              }  
            },
            modules: {
                product: {
                    namespaced: true,
                    state: {
                        id: 1
                    },
                    getters: {
                        getProductId(state) {
                            return state.id
                        }
                    }
                }
            }
        },
        loan: {
            namespaced: true,
            state: {
                money: 100000
            },
            getters: {
                getLoanMoney(state) {
                    return state.money
                }
            }
        },
        confirm: {
            state: {
                num: 100
            },
            getters: {
                getConfirmNum(state) {
                    return state.num
                }
            }
        }
    }
}
Copy the code
  • Direct use of

    We can access the required getter directly with the method vm.$store.getters. Xx.

    var mapGetters = Vuex.mapGetters
    
    var bill = {
        data() {
            return{... } }, computed: {getName() {
                return this.$store.getters.getName
            },
            getTradeType() {
                return this.$store.getters['trade/getTradeType']},getLoanMoney() {
                return this.$store.getters['loan/getLoanMoney']},getProductId() {
                return this.$store.getters['trade/product/getProductId']},getConfirmNum() {
                return this.$store.getters.getConfirmNum
            }
        },
        ...
    }
    Copy the code
  • Use the mapGetters helper function

    We can use the helper function – mapGetters to help us generate computed properties.

    var bill = {
        data() {
            return{... } }, computed: { ... mapGetters(['getName'.'getConfirmNum']),
            ...mapGetters({
                getTradeType: 'trade/getTradeType'
            }),
            ...mapGetters('trade', {
                getProductId: 'product/getProductId'
            }),
            ...mapGetters('loan', {
                'getLoanMoney': 'getLoanMoney'})},... }Copy the code
  • Use the createNamespacedHelpers helper function

    We can use helper functions such as createNamespacedHelpers and mapGetters to help us generate computed properties.

    CreateNamespacedHelpers can help you generate namespace-based helper functions.

    Var mapGetters = Vuex. MapGetters var tradeMapGetters = trade namespace Vuex.createNamespacedHelpers('trade'). MapGetters / / trade/product namespace mapGetters var productMapGetters = Vuex. CreateNamespacedHelpers ('trade/product'). MapGetters / / based on loan namespace mapGetters var loanMapGetters = Vuex. CreateNamespacedHelpers ('loan').mapGetters
    
    var bill = {
        data() {
            return{... } }, computed: { ... mapGetters(['getName'.'getConfirmNum']),
            ...tradeMapGetters({
                getTradeType: 'getTradeType'
            }),
            ...productMapGetters(['getProductId']),
            ...loanMapGetters(['getLoanMoney'])}}Copy the code

The use of mutation

Mutation is used to change the state of vuex, and its use is similar to that of state and getter, which we also illustrate with an example.

var options = {
    state: {
        name: 'zhangsan'
    },
    mutations: {
        setName(state, name) {
            state.name = name
        }
    },
    modules: {
        trade: {
            namespaced: true,
            state: {
                type: 'waiting'
            },
            mutations: {
                setTradeType(state, type) {
                    state.type = type
                }
            }
            modules: {
                product: {
                    namespaced: true,
                    state: {
                        id: 1
                    },
                    mutations: {
                        setProductId(state, id) {
                            state.id = id
                        }
                    }
                }
            }
        },
        loan: {
            namespaced: true,
            state: {
                money: 100000
            },
            mutations: {
                setLoanMoney(state, money) {
                    state.money = money
                }
            }
        },
        confirm: {
            state: {
                num: 10
            },
            mutations: {
                setConfirmNum(state, num) {
                    state.num = num
                }
            }
        }
    }
}
Copy the code
  • Direct use of

    In the component, we can submit mutation using this.store.com MIT (type, payload) to trigger the state state change.

    var bill = {
        ...
        methods: {
            setName() { 
                this.$store.commit('setName'.'bill')},setTradeType() { 
                this.$store.commit('trade/setTradeType'.'complete')},setProductId() {
                this.$store.commit('trade/product/setProductId'.'2')},setLoanMoney() {
                this.$store.commit('loan/setLoanMoney'.'2000000')},setConfirmNum() {
                this.$store.commit('setConfirmNum'.'20')}}}Copy the code
  • MapMutations auxiliary function is used

    We can use the auxiliary function mapMutations to help us generate the component method to submit the mutation, triggering the state state changes.

    var mapMutations = Vuex.mapMutations var bill = { ... Methods: {// Add to the Bill componentsetName method, mapping this.$store.commit('setName') // Add to the Bill componentsetConfirmNum method, maps this.$store.commit('setConfirmNum')
            ...mapMutations(['setName'.'setConfirmNum']), // add to the Bill componentsetThe TradeType method, maps this.$store.commit("trade/setTradeType') ... Mutations({setTradeType: 'trade/setTradeType'}), mapMutations({setTradeType: 'trade/setTradeType'}), maps this to Bill component setProductId.$store.commit('trade/product/setProductId') ... Mutations('trade', {setProductId: 'product/setProductId'}), mapMutations('trade', {setProductId: 'product/setProductId'}), adds setLoanMoney to bill component, maps this.$store.commit('loan/setLoanMoney') ... mapMutations('loan', { setLoanMoney: function(commit, money) { commit('setLoanMoney', money) } }), submit() { // this.$storeCommit (' elegantly-named setName ', 'li si) enclosing elegantly-named setName (' bill') / / this.$store.commit('setConfirmNum', 20)
                this.setConfirmNum(20)
                // this.$store.commit('trade/setTradeType', 'complete')
                this.setTradeType('complete')
                // this.$store.commit('trade/product/setProductId', 2345)
                this.setProductId('2345')
                // this.$store.commit('loan/setLoanMoney', 2000000) 
                this.setLoanMoney(20000000)
            }
    
        }
    }
    Copy the code
  • Use the createNamespacedHelpers helper function

    We can use helper functions -Createnamespaced helpers, mapMutations to help us generate the generation component method to commit the mutation, triggering the state state change.

    CreateNamespacedHelpers can help you generate namespace-based helper functions.

    MapMutations var mapMutations = Vuex. MapMutations // mapMutations var tradeMapMutations based on trade namespaces = Vuex.createNamespacedHelpers('trade'). MapMutations / / trade/product namespace mapMutations var productMapMutations = Vuex. CreateNamespacedHelpers ('trade/product'). MapMutations / / based on loan namespace mapMutations var loanMapMutations = Vuex. CreateNamespacedHelpers ('loan').mapMutations var bill = { ... Methods: {// Add to the Bill componentsetName method, mapping this.$store.commit('setName') // Add to the Bill componentsetConfirmNum method, maps this.$store.commit('setConfirmNum')
            ...mapMutations(['setName'.'setConfirmNum']), // add to the Bill componentsetThe TradeType method, maps this.$store.commit('trade/setTradeType')
            ...tradeMapMutations(['setTradeType']), // add to the Bill componentsetProductId, map this.$store.commit('trade/product/setProductId')
            ...productMapMutations(['setProductId']), // add to the Bill componentsetLoanMoney, mapping this.$store.commit('loan/setLoanMoney') 
            ...loanMapMutations({
                setLoanMoney: function(commit, money) {
                    commit('setLoanMoney', money)
                }
            }),
            submit() {
                // this.$store.commit('setName'.'bill')
                this.setName('bill')
                // this.$store.commit('setConfirmNum', 20)
                this.setConfirmNum(20)
                // this.$store.commit('trade/setTradeType'.'complete')
                this.setTradeType('complete')
                // this.$store.commit('trade/product/setProductId', 2345)
                this.setProductId('2345')
                // this.$store.commit('loan/setLoanMoney', 2000000) 
                this.setLoanMoney(20000000)
            }
        }
    }
    Copy the code

The use of the action

An action is similar to a mutation, except that an action commits a mutation instead of directly changing the state; An action can contain any asynchronous operation.

The use of action in components, similar to mutation, is illustrated by an example:

var options = {
    state: {
        name: 'zhangsan'
    },
    mutations: {
        setName(state, name) {
            state.name = name
        }
    },
    actions: {
        setName(context, name) {
            setTimeout(() => {
                context.commit('setName', name)
            }, 0)
        }
    }
    modules: {
        trade: {
            namespaced: true,
            state: {
                type: 'waiting'
            },
            mutations: {
                setTradeType(state, type) {
                    state.type = type
                }
            }
            actions: {
                setTradeType(context, type) {
                    setTimeout(() => {
                        context.commit('setTradeType'.type)
                    }, 1500)
                }
            }
            modules: {
                product: {
                    namespaced: true,
                    state: {
                        id: 1
                    },
                    mutations: {
                        setProductId(state, id) {
                            state.id = id
                        }
                    },
                    actions: {
                        setProductId(context, id) {
                            setTimeout(() => {
                                context.commit('setProductId', id)
                            }, 3000)
                        }
                    }
                }
            }
        },
        loan: {
            namespaced: true,
            state: {
                money: 100000
            },
            mutations: {
                setLoanMoney(state, money) {
                    state.money = money
                }
            },
            actions: {
                setLoanMoney(context, money) {
                    setTimeout(() => {
                        context.commit('setLoanMoney', money)
                    }, 2000)
                }
            }
        },
        confirm: {
            state: {
                num: 10
            },
            mutations: {
                setConfirmNum(state, num) {
                    state.num = num
                }
            },
            actions: {
                setConfirmNum(context, num) {
                    setTimeout(() => {
                        context.commit('setConfirmNum', num)
                    }, 5000)
                }
            }
        }
    }
}
Copy the code
  • Direct use of

    Within the component, we can directly dispatch the action by means of this.$store.Dispatch (type, payload), triggering the mutaion to be committed.

    var bill = {
        ...
        methods: {
            setName() { 
                this.$store.dispatch('setName'.'bill')},setTradeType() { 
                this.$store.dispatch('trade/setTradeType'.'complete')},setProductId() {
                this.$store.dispatch('trade/product/setProductId'.'2')},setLoanMoney() {
                this.$store.dispatch('loan/setLoanMoney'.'2000000')},setConfirmNum() {
                this.$store.dispatch('setConfirmNum'.'20')}}}Copy the code
  • Use the mapActions helper function

    We can use the helper function mapActions to help us generate component methods to dispatch the action that triggers the commit of the mutation.

    var mapActions = Vuex.mapActions var bill = { ... Methods: {// Add to the Bill componentsetName method, mapping this.$store.dispatch('setName') // Add to the Bill componentsetConfirmNum method, maps this.$store.dispatch('setConfirmNum')
            ...mapActions(['setName'.'setConfirmNum']), // add to the Bill componentsetThe TradeType method, maps this.$store.dispatch("trade/setTradeType') ... MapActions ({setTradeType: 'trade/setTradeType'}), // add setProductId to the Bill component, mapping this.$store.dispatch('trade/product/setProductId') ... MapActions ('trade', {setProductId: 'product/setProductId'}), // add setLoanMoney to the bill component, mapping this.$store.dispatch('loan/setLoanMoney') ... mapActions('loan', { setLoanMoney: function(dispatch, money) { dispatch('setLoanMoney', money) } }), submit() { // this.$store.dispatch('setName', 'lee Si ') this.setname (' Lee si ') // this.$store.dispatch('setConfirmNum', 20)
                this.setConfirmNum(20)
                // this.$store.dispatch('trade/setTradeType', 'complete')
                this.setTradeType('complete')
                // this.$store.dispatch('trade/product/setProductId', 2345)
                this.setProductId('2345')
                // this.$store.dispatch('loan/setLoanMoney', 2000000) 
                this.setLoanMoney(20000000)
            }
    
        }
    }
    
    
    Copy the code
  • Use the createNamespacedHelpers helper function

    We can use helper functions such as createNamespacedHelpers and mapActions to help us generate component methods to dispatch actions and trigger the commit of mutation.

    CreateNamespacedHelpers can help you generate namespace-based helper functions.

    MapActions based on global namespace var mapActions = Vuex. MapActions // mapActions based on trade namespace var tradeMapActions = Vuex.createNamespacedHelpers('trade'). MapActions / / trade/product namespace mapActions var productMapActions = Vuex. CreateNamespacedHelpers ('trade/product'). MapActions / / based on loan namespace mapActions var loanMapActions = Vuex. CreateNamespacedHelpers ('loan').mapActions var bill = { ... Methods: {// Add to the Bill componentsetName method, mapping this.$store.dispatch('setName') // Add to the Bill componentsetConfirmNum method, maps this.$store.dispatch('setConfirmNum')
            ...mapActions(['setName'.'setConfirmNum']), // add to the Bill componentsetThe TradeType method, maps this.$store.dispatch('trade/setTradeType')
            ...tradeMapActions(['setTradeType']), // add to the Bill componentsetProductId, map this.$store.dispatch('trade/product/setProductId')
            ...productMapActions(['setProductId']), // add to the Bill componentsetLoanMoney, mapping this.$store.dispatch('loan/setLoanMoney') 
            ...loanMapActions({
                setLoanMoney: function(dispatch, money) {
                    dispatch('setLoanMoney', money)
                }
            }),
            submit() {
                // this.$store.dispatch('setName'.'bill')
                this.setName('bill')
                // this.$store.dispatch('setConfirmNum', 20)
                this.setConfirmNum(20)
                // this.$store.dispatch('trade/setTradeType'.'complete')
                this.setTradeType('complete')
                // this.$store.dispatch('trade/product/setProductId', 2345)
                this.setProductId('2345')
                // this.$store.dispatch('loan/setLoanMoney', 2000000) 
                this.setLoanMoney(20000000)
            }
        }
    }
    Copy the code

other

  1. Do not modify state directly by using store.state = xx; otherwise, an exception will be thrown.

    class Store { ... // Access the state property of the store instance, essentially accessing store._vm._data.$$stateAttribute the getstate () {
            return this._vm._data.$$state
        }
        set state (v) {
            {
              assert(false, `use store.replaceState() to explicit replace store state.`); }}... replaceState (state) { this._withCommit(() => { this._vm._data.$$state= state; }); }}Copy the code

    Why can’t you change the state in set state? I don’t understand yet.

  2. The return value of fn can be listened for responsively through store.watch, and callback is called when the value changes.

    Fn receives the state of the store as the first argument, and its getter as the second argument. Whenever the state of vuex changes, FN executes it, and if the return value is different from the last time, the callback is fired.

  3. The store. Subscribe method is used to subscribe to the store mutation. When we submit the mutation through store.com MIT (type, payload), the mutation corresponding to type is triggered first, and then the SUBSCRIBE registration handler is triggered.

    Vue-devtools uses store.subscribe to track mutations submitted in the app.

    Store. Subscribe ((mutation, state) => {// devtoolHook devtoolhook.emit ('vuex:mutation', mutation, state);
    });
    Copy the code

    Store. subscribe is usually called by plugins.

  4. You can subscribe to the store’s actions using the store.subscribeAction method. The usage is as follows:

    store.subscribeAction({
        before: function(action, state) {
            ...
        },
        after: function(action, state) { ... }})Copy the code

    When we send action by means of store.dispatch(type, payload), the handler corresponding to before is first triggered, and then the mutation corresponding to type is triggered. Finally, the handler corresponding to after is fired.

    If the subscribeAction argument is a function, the default is the before handler, which is:

    store.subscribeAction(function(action, state) {
        ...
    })
    Copy the code
  5. The store.registerModule method allows you to dynamically register a new module.

    The first argument to a registerModule represents the name of the registered module and its parent module; The second argument is the configuration required to register the Module, which is {namespaced, state, getters, actions, mutations, modules}.

    // Register moduleA store.registerModule in the root Module('moduleA', {... }) // Register moduleA store.registerModule in the root Module(['moduleA'] and {... }) // Register moduleC store.registerModule in moduleB (['moduleA'.'moduleB'.'moduleC'] and {... })Copy the code

    Note that in the third example, an exception is thrown if moduleA does not exist in the root Module and moduleB does not exist in moduleA.

    After dynamic registration of a new module, the Store instance will collect state, getters, mutations, actions again, and reconstruct the VM instance for the Store instance (reactive principle).

  6. You can uninstall a dynamic module by using the store.unregisterModule method.

    UnregisterModule takes only one parameter, which represents the module to be uninstalled and its parent module.

    // Unload moduleA store.unregisterModule from the root Module('moduleA') // Uninstall moduleA Store.unregisterModule from the root Module(['moduleA']) // Uninstall moduleA Store.unregisterModule from moduleB (['moduleA'.'moduleB'.'moduleC'])
    Copy the code

    The unloaded module must be a dynamically registered module, otherwise an exception is thrown. When you uninstall a static module, the static Module is not removed from the parent module, but the state corresponding to the static module is removed. If the vUE component uses the state in the static Module, an error will be reported. This should be a bug in VUX.

    If the module to be uninstalled does not exist in the parent module, an error will be reported. There should be a bug here as well.

    An error will be reported if the parent module of the module to be uninstalled has the wrong path strength.

    After unloading a dynamically registered module, the Store instance will collect state, getters, mutations, and actions again, and reconstruct the VM instance for the Store instance (reactive principle).

  7. Devtools configuration items

    Turn DevTools on or off for the Store instance.

    If devtools is set to false, Vue-devtools cannot subscribe to store mutations via the store.subscribe method and cannot track store commits.