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中。
Props
,methods
,data
和computed
的初始化都是在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')
}