1. 程式人生 > 其它 >vuex:弄懂mapState、mapGetters、mapMutations、mapActions

vuex:弄懂mapState、mapGetters、mapMutations、mapActions

一、state

1.1 引入vuex 以後,我們需要在state中定義變數,類似於vue中的data,通過state來存放狀態

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: { //存放狀態
    nickname:'Simba',
    age:20,
    gender:'男'
  },
  mutations: {},
  actions: {},
  modules: {}
})

註冊兩個元件分別引入到app.vue中

<div id="app">
    <vabout> </vabout>
    <vhome> </vhome>
  </div>

vhome元件內容

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

vabout元件內容

<h1>{{$store.state.nickname}}:{{$store.state.age}}</h1>

如圖,顯示出顯示出相應的內容,有了vuex,我們不必在考慮元件之間的傳值,直接就可以通過$store來獲取不同的資料,但是如果需要vuex中的多個數據的這時候,這樣寫就太囉嗦了,我們可以將它定義在computed中。

Propsmethods,datacomputed的初始化都是在beforeCreated

created之間完成的。

例:

<template>
  <div class="home">
    {{nickname}}
  </div>
</template>
<script>
export default {
  name: 'home',
  computed:{
    nickname(){
      return this.$store.state.nickname
    }
  }
}
</script>

這樣引入就方便了很多。

1.2 mapState 輔助函式

1.1中的方法雖然引入的時候方便了,但是computed中定義的程式碼還是很多,而這時候vuex又給我們提供了更簡便的方法mapState方法

import {mapState} from 'vuex'
export default {
  name: 'home',
  computed: mapState(['nickname','age','gender'])
}

 

mapState(['nickname','age','gender']) //對映哪些欄位就填入哪些欄位

這一句程式碼就相當於下面這些

nickname(){return this.$store.state.nickname}
age(){return this.$store.state.age}
gender(){return this.$store.state.gender}

記住:用mapState等這種輔助函式的時候,前面的方法名和獲取的屬性名是一致的。

如果我們需要自定義一個計算屬性怎麼辦呢?怎麼新增呢?

畢竟現在已經成這樣了 computed: mapState(['nickname','age','gender'])

這時候我們就需要es6中的展開運算子:...

computed: {   //computed是不能傳引數的
  value(){
   return this.val/7
},
  ...mapState(['nickname','age','gender'])
}

二、getters

2.1 getters相當於vue中的計算屬性,通過getters進一步處理,得到我們想要的值,而且允許傳參,第一個引數就是state

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
 
export default new Vuex.Store({
  state: { //存放狀態
    nickname:'Simba',
    firstname:'張',
    lastname:'三豐',
    age:20,
    gender:'男',
    money:1000
  },
  getters:{
    realname(state){
      return state.firstname+state.lastname
    },
    money_us(state){
      return (state.money/7).toFixed(2)
    }
  },
  mutations: {},
  actions: {},
  modules: {}
})

vue部分

computed: {  //computed是不能傳引數的
 valued(){
   return this.value/7
 },
 ...mapGetters(['realname','money_us'])
}

三、Mutation

3.1 我們程式碼中定義的時候需要些mutations,它類似於vue中的methods,

mutations需要通過commit來呼叫其裡面的方法,它也可以傳入引數,第一個引數是state,第二個引數是載荷(payLoad),也就是額外的引數

程式碼如下

mutations: { //類似於methods
  addAge(state,payLoad){
     state.age+=payLoad.number
  }
}

template部分

<div class="home">
   <div><button @click="test">測試</button></div>
</div>

js部分

methods:{
 test(){
   this.$store.commit('addAge',{
     number:5
   })
 }
}

呼叫的時候第二個引數最好寫成物件形式,這樣我們就可以傳遞更多資訊。

但是,這樣寫還是會遇到同樣的問題,就是如果需要操作多個數據,就會變的麻煩,這時候我們就需要mapMutations,通過它將方法對映過來

3.2 mapMutations

跟mapState、mapGetters一樣

methods:{
 ...mapMutations(['addAge'])
}

mapMutations(['addAge'])這一句就相當於下面的程式碼

addAge(payLoad){
  this.$store.commit('addAge',payLoad)
}

引數我們可以在呼叫這個方法的時候寫入

<button @click="addAge({number:5})">測試</button>

這時候一些槓精就要說了,我為什麼要繞一圈,從mutations裡面去改state呢?我能不能直接改state呢?

比如這樣:

addAge(){
 this.$store.state.age +=5;
}

實際看結果也可以,那我為什麼從mutations裡面中轉一下呢?

原因如下:

① 在mutations中不僅僅能做賦值操作

② 作者在mutations中做了類似埋點操作,如果從mutations中操作的話, 能被檢測到,可以更方便用除錯工具除錯,除錯工具可以檢測到實時變化,而直接改變state中的屬性,則無法實時監測

注意:mutations只能寫同步方法,不能寫非同步,比如axios、setTimeout等,這些都不能寫,mutations的主要作用就是為了修改state的。

原因類似:如果在mutations中寫非同步,也能夠調成功,但是由於是非同步的,不能被除錯工具追蹤到,所有不推薦這樣寫,不利於除錯,這是官方的約定。

3.3 使用常量替代Mutation事件型別

把原本的方法名稱由字串轉變成常量

程式碼如下:

import Vue from 'vue'
import Vuex from 'vuex'
export const ADD_AGE ='addAge' 
Vue.use(Vuex)
export default new Vuex.Store({
  state: { //存放狀態
    nickname:'Simba',
    firstname:'張',
    lastname:'三豐',
    age:20,
    gender:'男',
    money:1000
  },
  getters:{ //類似於 computed
    realname:state =>state.firstname+state.lastname,
    money_us(state){
      return (state.money/7).toFixed(2)
    }
  },
  mutations: { //類似於methods
     [ADD_AGE](state,payLoad){
         state.age+=payLoad.number
     }
  },
  actions: { },
  modules: {}
})

將addAge方法名字定義為一個常量,當呼叫的時候直接引入

import {ADD_AGE} from '../store'
import {mapMutations} from 'vuex'
export default {
  methods:{
    ...mapMutations([ADD_AGE])
  }
}

這樣寫的好處:

① 不容易寫錯,字串容易寫錯,而且字串寫錯以後不會報錯位置,而用常量替代,如果寫錯,eslint可以提示錯誤位置

用常量替代mutations的時候我我們可以新建一個檔案(mutation_type.js)專門儲存這些常量

mutation_type.js部分

export default {
   ADD_AGE: ‘addAge’
}

然後再store/index.js中引入

import MUTATION_TYPES from ‘./mutation_type’(先引入)
export let MUTATION_TYPE=MUTATION_TYPES (再匯出)

這個地方有一個坑,不要將引入和匯出合併成一行程式碼:比如這樣

export { foo, bar } from 'my_module';
// 可以簡單理解為
import { foo, bar } from 'my_module';
export { foo, bar };

需要注意的是,兩者並不一樣,寫成一行以後,foo和bar實際上並沒有被匯入當前模組,只是相當於對外轉發了這兩個介面,導致當前模組不能直接使用foo和bar。

vue部分

import {MUTATION_TYPE} from '../store'
methods:{
  ...mapMutations([MUTATION_TYPE.ADD_AGE])
}

總結一下:

① 依賴state得到新的資料,用getters(跟computed一樣,只讀)

② 修改state的屬性值,就用mutations(同步操作)

四、actions

4.1 action類似於mutation

區別:action可以提交mutation

action也不要直接去操作state,而是去操作mutation

action包含非同步操作,類似於axios請求,可以都放在action中寫

action中的方法預設的就是非同步,並且返回promise

程式碼如下

store部分

actions: {
  getUserInfo(){
    return {
      nickname:'Simba',
      age:20
    }
  }
}

在actions中定義一個方法:getUserInfo,並且返回一個物件

vue部分

created(){
  var res = this.getUserInfo()
  console.log(res)
 
},
methods:{
  ...mapActions(['getUserInfo'])
}

在created中呼叫此方法,並將結果賦值給res,列印res,結果打印出Promise

這表明,在actions中的方法,預設就是非同步的,通過then獲取資料

mapActions(['getUserInfo']) 相當於以下程式碼

getUserInfo(){
  return this.$store.dispatch(‘getUserInfo’)
}

在實際開發當中,state裡面的屬性值是空的,當登入以後,再進行獲取對應的資訊。

登入以後,需要得到使用者資訊,那如何得到呢?

首先進入頁面的時候呼叫actions中的getUserInfo方法

程式碼如下

vue部分

created(){ this.getUserInfo()}
methods:{ ...mapActions([‘getUserInfo’])}

store部分

首先要想得到資料,那就相當於給state賦值,那首先想到的就是mutations來操作state,但是請求的介面都是axios非同步,所以就不能用mutations而是用actions,通過actions來操作mutations從而操作state

export default new Vuex.Store({
 state: { 
  nickname:‘’,
  age:0,
  gender:'',
  money:0
 },
 mutations: {
  setUerInfo(state,payLoad){
   state.nickname = payLoad.nickname
   state.age = payLoad.age
   state.gender = payLoad.gender
   state.money = payLoad.money
  }
},
actions: { //actions沒有提供state當引數
 async getToken({commit}){
   var res = await axios.get('/token介面')
   commit('setToken',res)
 },
async getUserInfo(context){ 
//context可以理解為它是整個Store的物件.類似於this.$store,
他裡面包含了state,getter,mutations,actions
  const res = await axios.get('/介面url')
  context.commit('setUerInfo',res) 
//相當於 this.$store.commit,第一個引數是方法名,第二個引數是要傳入的資料
  context.dispatch('getToken') 
//actions也可以呼叫自己的其他方法
    },
  }
})

執行過程,呼叫getUserInfo方法以後,進入actions,然後通過commit呼叫setUserInfo,將res(使用者資訊)作為引數傳入傳入進去,並將相對應的屬性值賦值給state,完成這一過程的操作。

getUserInfo的引數也可以用解構,這樣更方便

async getUserInfo({commit,dispatch}){ 
  const res = await axios.get('/介面url')
  commit('setUerInfo',res) 
  dispatch('getToken')
}