VUE狀態(tài)管理之Vuex

什么是Vuex

Vuex是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化?!?a target="_blank">官方介紹

對(duì)于第一次接觸Vuex的新手來(lái)說(shuō),看官方介紹多少還是有些迷糊。通俗一點(diǎn)來(lái)說(shuō),Vuex就是狀態(tài)管理工具、數(shù)據(jù)管理工具,幫我們的應(yīng)用管理著共享狀態(tài),解決了組件之間共享同一狀態(tài)的麻煩問(wèn)題的一個(gè)插件。


為什么要使用Vuex

可以使用的組件間傳值方案

  • 通過(guò)路由我們可以攜帶一些簡(jiǎn)單的參數(shù);
  • 使用prop和$emit來(lái)進(jìn)行通訊;
  • 使用EventBus(事件總線);
  • 更多的其他方式...

遇到的問(wèn)題

  • 我們先來(lái)描述一種場(chǎng)景,當(dāng)項(xiàng)目中多個(gè)組件都依賴同一個(gè)狀態(tài)的時(shí)候,如果是多層嵌套的父子組件,使用prop和$emit來(lái)進(jìn)行傳遞會(huì)顯得頗為繁瑣;如果是兄弟組件要想進(jìn)行傳遞,你或許可以使用EventBus(事件總線)來(lái)進(jìn)行交互,不得不承認(rèn),它非常的靈活,但是當(dāng)事件變多了以后維護(hù)起來(lái)會(huì)變得頗為麻煩,而且這種發(fā)布者-訂閱者的模式下,發(fā)布于訂閱必須是成對(duì)出現(xiàn)的,在訂閱事件的組件里,必須要手動(dòng)銷毀監(jiān)聽(tīng),不然會(huì)多次執(zhí)行。

Parent.vue

<template>
    <div>
      <h3>我是父組件,今年我{{ parentAge }}歲了!</h3>
      <Child :age="parentAge" @addParentAge="handlerAge"></Child>
    </div>
</template>
<script>
    import Child from './Child'
    export default {
        name:'Parent',
        components:{
            Child
        },
        data () {
            return {
                parentAge: 50
            }
        },
        methods: {
            handlerAge(age) {
                alert('好的,我知道了我的實(shí)際年齡是:', age)
            }
        }
     }
</script>

Child.vue

<template>
    <div>
      <h3>我是子組件,父組件告訴我他今年{{ age }}歲了!</h3>
      實(shí)際上,父組件今年的生日已經(jīng)過(guò)了,所以他的年齡應(yīng)該
      <button @click="addAge">加1</button>
    </div>
</template>
<script>
    export default {
        name:'Child',
        props: { 
            age: { type: Number, default: 0 } 
        },
        methods: {
            addAge() {
              this.$emit('addParentAge', this.age + 1);
            }
        }
    }
</script>
  • 有時(shí)候我們通過(guò)路由來(lái)傳遞某一種或者多種狀態(tài)(數(shù)據(jù)),如果狀態(tài)過(guò)多或某些狀態(tài)的內(nèi)容過(guò)長(zhǎng)的時(shí)候,這種方式顯然也行不通了。
navTo("/pages/discover/sub/activity-detail", { id: id });
  • 隨著項(xiàng)目越來(lái)越復(fù)雜,組件和數(shù)據(jù)之間的對(duì)應(yīng)關(guān)系也變得愈加混亂。

那么Vuex可以解決我們遇到的這些問(wèn)題么?接下來(lái)我們?cè)囈幌掳桑?/p>

怎么使用Vuex

安裝

npm install vuex --save // 使用 npm

yarn add vuex // 使用 yarn

當(dāng)然,你可以可以直接下載 vuex 或者CDN引用的方式來(lái)使用

<script src="/path/vue.js"></script>
<script src="/path/vuex.js"></script>

目錄和文件

src 目錄下按照你的需求構(gòu)建 store 目錄以及 js文件,我們可以在這里組裝模塊并導(dǎo)出 store

使用介紹

一、State
引入 Vuex 后,我們需要在 state 中定義變量,感覺(jué)這個(gè)有點(diǎn)類似 Vue 中的 data,通過(guò) state 來(lái)存放狀態(tài)。

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    // 存放狀態(tài)
    state: {
        age: 51,
        name: 'Parent',
        nickName: '父組件'
    },
    mutations: {
    },
    actions: {
    },
    modules: {
    }
})

這樣的話就不用考慮組件間的傳值,直接通過(guò) $store.state.age 來(lái)獲取年齡數(shù)據(jù)

<template>
    <div>
      <h3>我是子組件,父組件告訴我他今年{{ $store.state.age }}歲了!</h3>
    </div>
</template>

當(dāng)然,我們也可以把它定義在計(jì)算屬性 computed 中

template>
    <div>
      <h3>我是子組件,父組件告訴我他今年{{ age }}歲了!</h3>
    </div>
</template>
<script>
    export default {
        name:'Child',
        computed: {
            age() {
                return this.$store.state.age
            }   
        }
    }
</script>

mapState

上面的代碼,從使用上來(lái)說(shuō)基本可以滿足我們的需求,但是屬性多起來(lái)的話,感覺(jué)代碼寫(xiě)得還是有點(diǎn)繁瑣,好在 Vuex 還給我們提供了簡(jiǎn)便方法 mapState

import { mapState } from 'vuex'
export default {
    name:'Child',
    computed: mapState(['age', 'nickName', 'name'])
}
computed: mapState(['age', 'nickName', 'name'])

不得不說(shuō),真香!上面這一句代碼的作用,等同于下面這段代碼:

computed: {
    age() {
        return this.$store.state.age
    }
    nickName() {
        return this.$store.state.nickName
    }
    name() {
        return this.$store.state.name
    }
}

作為一個(gè)新手,可能還會(huì)有個(gè)疑問(wèn),你這樣寫(xiě)的話,如果我想再新增一個(gè)自定義的計(jì)算屬性怎么辦呢?雖然讓我少寫(xiě)了幾行代碼,但是確影響到我繼續(xù)添加計(jì)算屬性了。別慌,經(jīng)過(guò)參考項(xiàng)目中大佬的代碼發(fā)現(xiàn)是使用了 ES6 的展開(kāi)運(yùn)算符“...”

computed: {
    realAge() {
        return this.age + 1
    }
    ...mapState(['age', 'nickName', 'name'])
}

二、Getters
從各種資料以及個(gè)人的實(shí)際操作過(guò)程中,可以這樣來(lái)理解,getters 相當(dāng)于 Vue 中的計(jì)算屬性,通過(guò) getters 可以獲取我們想要取得的值。

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    // 存放狀態(tài)
    state: {
        age: 51,
        name: 'Parent',
        nickName: '父組件'
    },
    getters: {
        desc(state) {
            return state.name + '的年齡是' + state.age
        }
    },
    mutations: {
    },
    actions: {
    },
    modules: {
    }
})

然后可以在 Vue 這樣使用它: <div>{{ desc }}</div>

computed: {
    realAge() {
        return this.age + 1
    }
    ...mapGetters(['desc'])
}

三、Mutations

通過(guò)提交 mutation 可以實(shí)現(xiàn)更改 Vuex 的 store 中的狀態(tài)。有沒(méi)有覺(jué)得 Mutations 有點(diǎn)類似 Vue 的 methods 事件?每一個(gè) mutation 都有一個(gè)字符串的事件類型(type)和一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)也就是我們進(jìn)行狀態(tài)更改的地方,它可以傳入?yún)?shù),第一個(gè)參數(shù)時(shí) state,第二個(gè)參數(shù)時(shí) payload,直接來(lái)看看代碼吧!

mutations: {
    addAge(state, payload) {
        state.age += payload.years
    }
},

在頁(yè)面部分添加一個(gè)測(cè)試方法

<template>
    <div>
      <h3>3年過(guò)去了,多少歲了?</h3>
      <div>
        <button @click="tellMe">點(diǎn)擊我告訴你</button>
      </div>
    </div>
</template>

payload 參數(shù)可以是對(duì)象類型,這樣可以傳遞更復(fù)雜的參數(shù)

methods: {
    tellMe() {
        this.$store.commit('addAge', { years: 3 })
    }
}

如果我們需要操作多個(gè)數(shù)據(jù)的時(shí)候,代碼也會(huì)有點(diǎn)多,和 mapState、mapGetters 類似,mutations 也有映射函數(shù) mapMutations,使用它可以幫助我們簡(jiǎn)化代碼。

mapMutations

直接上代碼

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

上述代碼相當(dāng)于下面這段代碼

import { mapMutations } from 'vuex'
export default {
    methods: {
        addAge(payload) {
            this.$store.commit('addAge', payload)
        }
    }
}

然后可以發(fā)起調(diào)用

methods: {
    tellMe() {
        this.addAge({ years: 3 })
    }
}

甚至你也可以這樣調(diào)用

<button @click="addAge({ years: 3 })">點(diǎn)擊我告訴你</button>

注意點(diǎn):mutations 不推薦寫(xiě)異步方法,比如 axios、setTimeout 等,mutations主要是的作用是修改 state。

使用常量替代事件類型

查看原來(lái)項(xiàng)目中的代碼,發(fā)現(xiàn)可以把方法名稱用常量來(lái)代替。這樣的話可以很大程度上避免寫(xiě)錯(cuò),eslint也會(huì)提示錯(cuò)誤位置。

import Vue from 'vue'
import Vuex from 'vuex'
export const ADD_AGE = 'addAge'
Vue.use(Vuex)
export default new Vuex.Store({
    // 存放狀態(tài)
    state: {
        age: 51,
        name: 'Parent',
        nickName: '父組件'
    },
    getters: {
        desc(state) {
            return state.name + '的年齡是' + state.age
        }
    },
    mutations: {
        [ADD_AGE](state, payload) {
            state.age += payload.years
        }
    },
    actions: {
    },
    modules: {
    }
})

把 addAge 方法名定義為一個(gè)常量,調(diào)用的時(shí)候直接引入進(jìn)來(lái)

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

通過(guò)閱讀 H5 的項(xiàng)目代碼,發(fā)現(xiàn)是新建了一個(gè) types.js 的文件,專門(mén)用來(lái)存儲(chǔ)這些常量,然后在需要使用的 store js 文件里引入, 這樣看起來(lái)確實(shí)統(tǒng)一一些更有利于管理,具體代碼這里就不貼了,參見(jiàn)項(xiàng)目。

四、Actions

Action 類似于 mutation,區(qū)別在于 Actions 提交的是 mutaion,而不是直接修改狀態(tài) state,另外 Actions 可以包含異步操作。

先在 actions 中定義一個(gè)方法,并且返回一個(gè)對(duì)象

actions: {
    getUserInfo() {
        return {
            age: 51,
            name: 'Parent',
            nickName: '父組件'
        }
    }
}

然后在 Vue 文件的 created 中調(diào)用該方法,并且將結(jié)果賦值給 userInfo,并且打印。從控制臺(tái)來(lái)看,是一個(gè) Promise ,這說(shuō)明 actions 中的方法默認(rèn)就是異步的。

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

Action 通過(guò) store.dispatch 方法觸發(fā)

this.$store.dispatch('getUserInfo')

而上述代碼中的 ...mapActions(['getUserInfo']) 則相當(dāng)于以下代碼

getUserInfo() {
    return this.$store.dispatch('getUserInfo')
}

在實(shí)際項(xiàng)目中,一般 state 的屬性值都是空的,在完成登錄操作后才可以獲取到用戶信息。下面簡(jiǎn)單看一下模擬的代碼:

created() {
    this.getUserInfo()
}
methods: {
    ...mapActions(['getUserInfo'])
}
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    // 存放狀態(tài)
    state: {
        age: 51,
        name: 'Parent',
        nickName: '父組件'
    },
    mutations: {
        setUserInfo(state, payload) {
            state.age = payload.age
            state.name = payload.name           
            state.nickName = payload.nickName
        }
    },
    actions: {
        async getUserInfo(context) {
            const res = await axios.get('/api/fetchUserInfo')
            context.commit('setUserInfo', res)
        }
    },
    modules: {
    }
})

簡(jiǎn)單分析一下,如果要給 state 賦值,肯定會(huì)先想到通過(guò) mutations 來(lái)操作 state,但是數(shù)據(jù)是從網(wǎng)絡(luò)請(qǐng)求接口異步去取得的,所以還不能用 mutations 而是用 actions,也就是說(shuō):通過(guò) actions 來(lái)操作 mutations,從而再去操作 state。

在運(yùn)行的過(guò)程中,先是調(diào)用 getUserInfo 方法,進(jìn)入了actions,然后通過(guò) commit 調(diào)用了 setUserInfo,并且把 res(用戶信息)作為參數(shù)傳進(jìn)去,最后將對(duì)應(yīng)的屬性逐一賦值給 state。

簡(jiǎn)單總結(jié):Mutations 負(fù)責(zé)存入,只要分發(fā)給我我就存;Actions 負(fù)責(zé)中間處理,處理完成我就給到 Mutations,Mutations 你要怎么存我不管,所有對(duì) state 狀態(tài)賦值都是由 Mutations 來(lái)操作的;而 Getters 則只負(fù)責(zé)取,不進(jìn)行修改。

五、Modules

想象一下,隨著項(xiàng)目的發(fā)展,store文件的東西越來(lái)越多,變得非常臃腫,這種單一狀態(tài)樹(shù)的做法變得不太好維護(hù)。 Vuex 為了解決上述問(wèn)題,允許我們將 store 分割成模塊(module),每個(gè)模塊都可以擁有自己的 state、mutation、action 以及 getter,甚至還能嵌套子模塊。

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

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

const store = new Vuex.Store({
    modules: {
        user: userModule,
        customer: customerModule
    }
})
store.state.user  // userModule的狀態(tài)
store.state.customer // customerModule的狀態(tài)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、什么是Vuex Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有...
    紫月凌楓閱讀 10,262評(píng)論 0 16
  • 1、vuex簡(jiǎn)介以及創(chuàng)建一個(gè)簡(jiǎn)單的倉(cāng)庫(kù) vuex是專門(mén)為vue框架而設(shè)計(jì)出的一個(gè)公共數(shù)據(jù)管理框架,任何組件都可以通...
    不見(jiàn)_長(zhǎng)安閱讀 420評(píng)論 0 0
  • 可用于登錄的狀態(tài)管理 一、vuex包含五個(gè)基本的對(duì)象: state:存儲(chǔ)狀態(tài),也就是變量; getters:派生狀...
    林夕Yola閱讀 766評(píng)論 0 0
  • 前言 ? 本文假設(shè)讀者已經(jīng)了解vue的基礎(chǔ)語(yǔ)法,能夠使用vue編寫(xiě)出簡(jiǎn)單的頁(yè)面 Vue 組件間的通信 為了更全面認(rèn)...
    果汁涼茶丶閱讀 1,360評(píng)論 0 10
  • 一、vuex的簡(jiǎn)單介紹 Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式,簡(jiǎn)單的來(lái)說(shuō)作用是:可以簡(jiǎn)單...
    曼少女閱讀 474評(píng)論 0 0

友情鏈接更多精彩內(nèi)容