Vue learning-in-depth analysis of vuex

One, State

Vuex is Vue's state management tool, in order to more conveniently realize the shared state of multiple components.

1. Installation

npm install vuex --save

2. Use

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

Vue.use(Vuex);

const store = new Vuex.Store({
    
    
  state: {
    
    
    count: 0
  }
})

new Vue({
    
    
  store,
})

3.State

In a single state tree, using one object contains all application-level states.

1. Get Vuex status in Vue component

Through the store option, Vuex provides a mechanism to "inject" state from the follower component into each child component (call Vue.use(Vuex)).

By registering the store option in the root instance, the store instance will be injected into all sub-components under the root component, and the sub-components can be accessed through this.$store.

<div class="home">
  {
   
   { $store.state.count }}
</div>

2. mapState helper function

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

import {
    
     mapState } from 'vuex';

computed: {
    
    
  ...mapState(['count']),
},

Use different names:

computed: {
    
    
  ...mapState({
    
    
    storeCount: state => state.count,
    // 简写
    storeCount: 'count', // 等同于 state => state.count
  }),
},

二、Vuex_Getter

The calculated properties of the store. The return value of the getter will be cached according to its dependencies, and will only be recalculated when its dependency value changes.

Getter receives state as its first parameter and getters as its second parameter.

getters: {
    
    
  doubleCount (state) {
    
    
    return state.count * 2;
  }
}

1. Access through attributes

Getter will be exposed as store.getters object:this.$store.getters.doubleCount

2. Access by method

You can also let the getter return a function to pass parameters to the getter

getters: {
    
    
  addCount: state => num => state.count + num;
}
this.$store.addCount(3);

3.mapGetters helper function

import {
    
     mapsGetters } from 'vuex';

export default {
    
    
  computed: {
    
    
    ...mapGetters([
      'doubleCount',
      'addCount',
    ])
  }
}

If you want to give a getter property another name, use the object form:

mapGetters({
    
    
  // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
  storeDoubleCount: 'doubleCount'
})

Three, Vuex_Mutation

The only way to change the state in the Vuex store is to submit a mutation.

const store = new Vuex.Store({
    
    
  state: {
    
    
    count: 1
  },
  mutations: {
    
    
    increment (state) {
    
    
      // 变更状态
      state.count++
    }
  }
})

You cannot directly call a mutation handler. This option is more like event registration: "When a incrementmutation of type is triggered , call the secondary function.":

this.$store.commit('increment');

1. Submit Mutation in the component

Except that in the assembly this.$store.commit('xxx')to submitting mutation, it may also be used mapMutations helpers:

import {
    
     mapMutations } from 'vuex'

export default {
    
    
  // ...
  methods: {
    
    
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    ]),
    ...mapMutations({
    
    
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

2. Submit the payload (Payload)

You can pass additional parameters to store.commit, namely the payload of the mutation:

mutations: {
    
    
  increment (state, n) {
    
    
    state.count += n
  }
}
store.commit('increment', 10)

In most cases, the payload should be an object, so that it can contain multiple fields and the recorded mutation will be more readable:

mutations: {
    
    
  increment (state, payload) {
    
    
    state.count += payload.amount
  }
}
store.commit('increment', {
    
    
  amount: 10
})

3. Object style submission method

Another way to submit a mutation is to directly use the object that contains the type attribute:

store.commit({
    
    
  type: 'increment',
  amount: 10
})

When using the object-style submission method, the entire object is passed to the mutation function as the payload, so the handler remains unchanged:

mutations: {
    
    
  increment (state, payload) {
    
    
    state.count += payload.amount
  }
}

4. Use constants instead of Mutation event types

Putting these constants in a separate file allows your code collaborators to see at a glance the mutations contained in the entire app:

// mutation-types.js
export const COUNT_INCREMENT = 'COUNT_INCREMENT'
// store.js
import Vuex from 'vuex'
import {
    
     COUNT_INCREMENT } from './mutation-types'

const store = new Vuex.Store({
    
    
  state: {
    
     ... },
  mutations: {
    
    
    [COUNT_INCREMENT] (state) {
    
    
      // ...
    }
  }
})

It's up to you to use constants, which can be helpful in large projects that require multi-person collaboration.

5. Mutation is subject to Vue's response rules

Since the state in the Vuex store is responsive, when we change the state, the Vue component that monitors the state will also be automatically updated. This also means that mutations in Vuex also need to follow some precautions as with Vue:

  • It is best to initialize all required attributes in your store in advance.
  • When you need to add new properties to the object, you should
    • Use Vue.set(obj,'newProp', 123), or
    • Replace old objects with new objects. For example, using the object expansion operator we can write:
      state.obj = {
              
               ...state.obj, newProp: 123 }
      

6. Form processing

When using v-model on Vuex's state, Vue will throw an error because it will directly change the value of the state.

If you want to use the two-way data function, you need to simulate a v-model yourself: :value=“msg” @input=“updateMsg”.

1. Two-way binding calculation properties

The above approach is much more cumbersome than the v-model itself, so we can also use the setter of the calculated property to achieve two-way binding:

<input v-model="msg">
computed: {
    
    
  msg: {
    
    
    get () {
    
    
      return this.$store.state.obj.msg;
    },
    set (value) {
    
    
      this.$store.commit(UPDATE_MSG, {
    
     value });
    }
  }
}

7. Mutation must be a synchronous function

Remember that mutation must be a synchronous function . why?

mutations: {
    
    
  [COUNT_INCREMENT] (state) {
    
    
    setTimeout(() => {
    
    
      state.count ++;
    }, 1000)
  },
}

Executing the upper code, we will find that the operation of changing the state is executed in the callback function, which will make our code difficult to debug in devtools: when the mutation is triggered, the callback function has not been called yet, devtools does not know When the callback function is actually called, any state changes made in the callback function are untraceable.

8. Strict mode

To enable strict mode, only need to pass strict: true when creating the store:

const store = new Vuex.Store({
    
    
  // ...
  strict: true
})

In strict mode, whenever a state change occurs and is not caused by the mutation function, an error will be thrown. This ensures that all state changes can be tracked by debugging tools.

1. Development environment and release environment

Do not enable strict mode in the publishing environment! Strict mode will deeply monitor the state tree to detect non-compliant state changes. Make sure to turn off strict mode in the release environment to avoid performance loss.

const store = new Vuex.Store({
    
    
  // ...
  strict: process.env.NODE_ENV !== 'production'
})

Four, Vuex_Action

Action is similar to mutation, the difference is:

  • Action submits a mutation instead of directly changing the state.
  • Action can contain any asynchronous operation

The Action function accepts a context object with the same methods and properties as the store instance, so you can call context.commit to submit a mutation, or get state and getters through context.state and context.getters:

const store = new Vuex.Store({
    
    
  state: {
    
    
    count: 0
  },
  mutations: {
    
    
    increment (state) {
    
    
      state.count++
    }
  },
  actions: {
    
    
    increment (context) {
    
    
      context.commit('increment')
    }
  }
})

1. Distribute Action

store.dispatch('increment')

Although similar to mutation, asynchronous operations can be performed in action, but not in mutation! ! !

actions: {
    
    
  incrementAsync ({
    
     commit }) {
    
    
    setTimeout(() => {
    
    
      commit('increment')
    }, 1000)
  }
}

2. Combination Action

Action is usually asynchronous, so how do you know when the action ends?

actions: {
    
    
  actionA ({
    
     commit }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      setTimeout(() => {
    
    
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}
store.dispatch('actionA').then(() => {
    
    
  // ...
})

3.Vuex management mode

Insert picture description here

五、Vuex_Module

Due to the use of a single state tree, all states of the application will be concentrated into a relatively large object. When the application becomes very complex, the store object may become quite bloated.

To solve the above problems, Vuex allows us to divide the store into modules. Each module has its own state, mutation, action, and getter.

modules: {
    
    
  a,
  b
}
  • 获取 state:this.$store.state.moduleName.xxx
  • 获取 getter:this.$store.getters.xxx
  • Submit mutation: this.$store.commit('xxx');
  • Distribution action: this.$store.dispatch('xxx');
  • You can get getters, mutations, and actions through mapXXX, but you can't get state. If you want to get state in this way, you need to add a namespace.

1. Namespace

You can make it a module with namespaces by adding namespaced: true.

  • 获取 state:this.$store.state.moduleName.xxx
  • 获取 getter:this.$store.[‘moduleName/getters’].xxx
  • 提交 mutation:this.$store.commit(‘moduleName/xxx’);
  • 分发 action:this.$store.dispatch(‘moduleName/xxx’);
  • You can get state, getters, mutations, and actions through mapXXX.

2. The local state of the module

For the mutation and getter inside the module, the first parameter received is the local state object of the module.

Similarly, for actions inside the module, the local state is exposed through context.state, and the root node state is context.rootState.

For the getter inside the module, the root node state will be exposed as the third parameter.

Guess you like

Origin blog.csdn.net/xun__xing/article/details/108580007