[Front-end vue interview] vuex

What is Vuex?

Vuex is a state management pattern developed specifically for Vue.js applications. It uses centralized storage to manage the state of all components of the application, and uses corresponding rules to ensure that the state changes in a predictable way.

vuex flow chart

Insert image description here

The difference between Vuex and simple global objects

  • Vuex's state storage is reactive. When a Vue component reads state from the store, if the state in the store changes, the corresponding component will be efficiently updated accordingly.
  • You cannot directly change the state in the store. The only way to change the state in the store is to explicitly commit a mutation. This allows us to easily track every state change, allowing us to implement some tools to help us better understand our application.

State

Provides a unique public data source, and all shared data is stored in the store's state, similar to data

import Vue from 'vue'
import Vuex from 'vuex'
 
Vue.use(Vuex)
 
export default new Vuex.Store({
    
    
  state: {
    
    
    name:"张三",
    age:12,
    count:0
  },
})

//调用
this.$store.state.count

//template中直接用
<div>{
    
    {
    
    $store.state.count}}</div>

由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态

mapState

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

import {
    
     mapState } from "vuex";
...
computed: mapState({
    
    
      // 箭头函数可使代码更简练
      name: state => state.name,

       // 传字符串参数 'age' 等同于 `state => state.age`
      age: 'age',

      // 为了能够使用 `this` 获取局部状态,必须使用常规函数
      countPlusAge (state) {
    
    
        return state.age + this.count
      }
    })

When the name of the mapped calculated attribute is the same as the name of the child node of state, we can also pass a string array to mapState.

computed: mapState([
  // 映射 this.age 为 store.state.age
  'age',
   // 映射 this.name 为 store.state.name
  'name'
])
// ===等同于===
computed:  mapState({
    
    age:'age', name: 'name'})

Mixed with local computed properties

computed: {
    
    
  localComputed () {
    
     /* ... */ },

  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    
    
    // ...
  })
}

Getter

Vuex allows us to define "getters" in the store (which can be thought of as computed properties of the store). Just like computed properties, the return value of a getter
is cached based on its dependencies, and is only recomputed when its dependency values ​​change. Learning to use Getter can avoid us repeatedly getting data through state
.

Getter accepts state as its first parameter. We can process the data in state accordingly and finally return the data we want:

//vuex
getters: {
    
    
      skillList: state => {
    
    
        return state.skill.filter(item => item.type === 1)
      }
    }

//页面
computed: {
    
    
      skillList() {
    
    
        return this.$store.getters.skillList
      }
    }   
    

Getters can also accept other getters as second arguments:

getters: {
    
    
      skillList: state => {
    
    
        return state.skill.filter(item => item.type === 1)
      },
      skillCount: (state, getters) => {
    
    
        return getters.skillList.length
      },
    }

computed: {
    
    
      skillList() {
    
    
        return this.$store.getters.skillList
      },
      count() {
    
    
        return this.$store.getters.skillCount
      }
    }

In addition to returning data directly, the getter can also pass parameters to the getter by letting the getter return a function. Very useful when querying arrays in the store.

getters: {
    
    
      skillList: state => (type) => {
    
    
        return state.skill.filter(item => item.type === type)
      },
      skillCount: (state, getters) => (type) => {
    
    
        return getters.skillList(type).length
      },
    }

computed: {
    
    
      skillList() {
    
    
        return this.$store.getters.skillList(2)
      },
      count() {
    
    
        return this.$store.getters.skillCount(2)
      }
    }

The mapGetters helper function simply maps the getters in the store to local computed properties:

computed: {
    
    
      ...mapGetters([
        'skillList',
        'skillCount'
      ])
    }

Mutation

The only way to change state in a Vuex store is to submit a mutation. Mutations in Vuex are very similar to events: each mutation has a string event type (type) and a callback function (handler). This callback function is where we actually make the state changes, and it accepts state as the first parameter:

  state: {
    
    
    count: 1
  },
  mutations: {
    
    
    increment (state) {
    
    
      // 变更状态
      state.count++
    }
  }

trigger mutation

store.commit('increment')

You can pass additional parameters to store.commit, which are the mutation's payload:

mutations: {
    
    
  incrementByCount (state, n) {
    
    
    state.count = state.count + n
  }
}


store.commit('incrementByCount', 10)

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

// 定义 mutation
mutations: {
    
    
  incrementByCount (state, payload) {
    
    
    state.count = state.count  + payload.count
  }
}
// 触发 mutation
store.commit('incrementByCount', {
    
    
  count: 10
})

Object style submission

store.commit({
    
    
  type: 'incrementByCount',
  count: 10
})

mapMutations

mapMutations receives parameters in array format

...mapMutations([
  // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
  'increment'
]),

mapMutations receives parameters in object format

...mapMutations({
    
    
  [别名]: [Mutation type] 
})
...mapMutations({
    
    
  add: 'increment'
})

Action

Action is similar to Mutation, except that:

  • Action submits a mutation rather than directly changing the state.
  • Action can contain any asynchronous operation. During the use of vuex, we can merge multiple mutations into one action, or perform asynchronous operations through action.

Basic usage

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

  state: {
    
    
    count: 1
  },
  mutations: {
    
    
    increment (state) {
    
    
      state.count++
    }
  },
  actions: {
    
    
    // 同步 action
    increment (context) {
    
    
      context.commit('increment')
    },
    // 异步 action
    incrementAsync (context) {
    
    
      setTimeout(() => {
    
    
        context.commit('increment')
      }, 1000)
    }
  }

In practice, we often use ES2015 parameter destructuring to simplify the code (especially when we need to call commit many times):

actions: {
    
    
  increment ({
     
      commit }) {
    
    
    commit('increment')
  }
}

dispatch

Action dispatches Action through store.dispatch method

store.dispatch('increment')

Submit payload

You can pass additional parameters to store.dispatch, which are the payloads of Actions:

action: {
    
    
  increment ({
     
     commit}, payload) {
    
    
    // 具体 action 内容
  }
}


store.dispatch('increment', {
    
    count: 10})

//对象风格的提交方式
store.dispatch({
    
    
  type: 'increment',
  count: 10
})


mapActions

The mapActions helper function helps us simplify the writing of submitted actions.

//mapActions 接收数组格式的参数
...mapActions([
  // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
  'increment'
]),


//mapActions 接收对象格式的参数
//在某些情况,我们需要对 Action 中的函数名重命名以避免和组件内部的变量冲突,这时候我们可以使用对象的方式接收参数:
...mapActions({
    
    
  [别名]: [Action name] 
})
// 例:将 `this.add()` 映射为 `this.$store.dispatch('increment')`
...mapActions({
    
    
  add: 'increment'
})

Combination Action

Action is usually asynchronous. Sometimes we need to know when the action ends and perform other corresponding operations after it ends. More importantly, we can combine multiple actions to handle more complex asynchronous processes.
First of all, you need to understand that store.dispatch can handle the Promise returned by the handler function of the triggered action, and store.dispatch still returns Promise:

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

Now we can:

store.dispatch('actionA').then(() => {
    
    
  // ...
})

It can also be done in another action:

actions: {
    
    
  // ...
  actionB ({
     
      dispatch, commit }) {
    
    
    return dispatch('actionA').then(() => {
    
    
      commit('someOtherMutation')
    })
  }
}

Finally, if we utilize async /await, we can compose actions as follows:

// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
    
    
  async actionA ({
     
      commit }) {
    
    
    commit('increment', await getData())
  },
  async actionB ({
     
      dispatch, commit }) {
    
    
    await dispatch('actionA') // 等待 actionA 完成
    commit('increment', await getOtherData())
  }
}

Modules

When encountering large-scale projects, the amount of data is large, and the store will become very bloated.

In order to solve the above problems, Vuex allows us to split the store into modules. Each module has its own state, mutation, action, getter, and even nested submodules.

const moduleA = {
    
    
  state: {
    
     ... },
  mutations: {
    
     ... },
  actions: {
    
     ... },
  getters: {
    
     ... }
}

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

const store = new Vuex.Store({
    
    
  modules: {
    
    
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态


Guess you like

Origin blog.csdn.net/qq_37215621/article/details/133963062