好久沒(méi)有寫(xiě)文章了,感覺(jué)自己就這樣稀里糊涂的過(guò)了好久了。最近在研究小程序 本來(lái)想等小程序有所得之后再來(lái)寫(xiě)點(diǎn)什么。但是發(fā)現(xiàn)自己研究來(lái)研究去 ,感覺(jué)沒(méi)沒(méi)有什么好寫(xiě)的。但是又不想丟棄掉自己寫(xiě)作的一個(gè)習(xí)慣。剛好前段時(shí)間在重構(gòu)代碼的時(shí)候,發(fā)現(xiàn)了之前封裝代碼的時(shí)候出現(xiàn)了很多的奇怪的寫(xiě)法,和很多之前沒(méi)考慮到的漏洞,多少還是有點(diǎn)心得的,寫(xiě)下這個(gè)文章呢?和大家交流一下
一、 為什么要用vuex
首先我們談到一個(gè)技術(shù)肯定是要從以下幾個(gè)方面來(lái)進(jìn)行探究的:
- 什么情況下要用這項(xiàng)技術(shù)
- 這項(xiàng)技術(shù)相對(duì)于其他技術(shù)的優(yōu)勢(shì)
我這里呢?就從這兩個(gè)方面來(lái)開(kāi)始,逐次、深層次的介紹vuex。
首先現(xiàn)今幾大主流的框架:react,vue,angluar包括小程序,無(wú)以復(fù)加的都提出一個(gè)組件化的思想。這樣做的好處是:高內(nèi)聚、可重用、可互換、可組合等優(yōu)勢(shì),但同時(shí)也在開(kāi)發(fā)中帶來(lái)了許多新的問(wèn)題,其中最重大的研究課題是:組件間的通信。
前端也差不多開(kāi)發(fā)了一年半的時(shí)間了,主流的幾種vue的組件間傳遞信息的方式,無(wú)非有以下幾種:
1、 通過(guò) $emit和props在父子組件中進(jìn)行數(shù)據(jù)傳遞。
2、 用eventbus(全局事件)在中進(jìn)行傳遞。
3、 利用localstorage、sessionstorage等存儲(chǔ)手段來(lái)進(jìn)行傳遞。
毫無(wú)疑問(wèn),這些個(gè)方式都能完成我們所要的需求。但是在某種程度上面或多或少都有一些問(wèn)題。例如:
1、 利用$emit和props在兄弟關(guān)系嵌套比較深的情況之下,代碼書(shū)寫(xiě)量將會(huì)大大的增加。
2、 全局事件在使用的時(shí)候可能被被多次觸發(fā)、銷(xiāo)毀時(shí)機(jī)掌握等等一系列的問(wèn)題,同時(shí)作為全局事件所有頁(yè)面都能監(jiān)聽(tīng)、掌控事件可能會(huì)存在安全性問(wèn)題等等...
3、 ...
既然前面說(shuō)了這么多其他技術(shù)的壞話,現(xiàn)在就要來(lái)介紹一下今天文章的主角了。
vuex--是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
首先來(lái)明確幾個(gè)概念:
一、狀態(tài)管理模式

上面是一個(gè)vue簡(jiǎn)單的狀態(tài)管理應(yīng)用,其中應(yīng)該包含有:
- state,驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源;
- view,以聲明方式將 state 映射到視圖;
- actions,響應(yīng)在 view 上的用戶輸入導(dǎo)致的狀態(tài)變化。
當(dāng)然上面只是一個(gè)最簡(jiǎn)單的“單向數(shù)據(jù)流”的示意圖。但是當(dāng)我們存在很多的組件嵌套的時(shí)候,可能會(huì)存在這樣幾個(gè)問(wèn)題:
- 多個(gè)視圖依賴于同一狀態(tài)。
- 來(lái)自不同視圖的行為需要變更同一狀態(tài)。
顯然用前面的哪幾個(gè)技術(shù)可能會(huì)將頁(yè)面的狀態(tài)變得更加復(fù)雜和代碼邏輯繁瑣,不利于代碼的維護(hù)和深層次開(kāi)發(fā)。在這種大環(huán)境下面,設(shè)計(jì)師們專門(mén)為vuex設(shè)計(jì)了一款狀態(tài)管理庫(kù),以利用 Vue.js 的細(xì)粒度數(shù)據(jù)響應(yīng)機(jī)制來(lái)進(jìn)行高效的狀態(tài)更新。

上面便是vuex的工作流程,下面我們就開(kāi)始簡(jiǎn)單的來(lái)介紹一下vuex的一些構(gòu)成。
二、 vuex的簡(jiǎn)單介紹
說(shuō)到vuex的介紹呢?我們就先從代碼的引入開(kāi)始:
npm install vuex --save
import Vue from "vue";
import Vuex from "vuex"
Vue.use(Vuex)
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
最后再main.js中:
import store from "./store"
var app=new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
看了上面的代碼,首先來(lái)解釋一下:new Vuex.Store({}) 表示創(chuàng)建一個(gè)Vuex實(shí)例,通常情況下,他需要注入到Vue實(shí)例里. Store是Vuex的一個(gè)核心方法,字面上理解為“倉(cāng)庫(kù)”的意思。Vuex Store是響應(yīng)式的,當(dāng)Vue組件從store中讀取狀態(tài)(state選項(xiàng))時(shí),若store中的狀態(tài)發(fā)生更新時(shí),他會(huì)及時(shí)的響應(yīng)給其他的組件(類似雙向數(shù)據(jù)綁定) 而且不能直接改變store的狀態(tài),改變狀態(tài)的唯一方法就是,顯式地提交更改(mutations選項(xiàng))
上面就將vuex的四個(gè)核心選項(xiàng):state mutations getters actions說(shuō)了出來(lái)。
2.1、 state:
首先是state,如何來(lái)獲取state的值呢?一般是將這個(gè)值放置在computer里面,這樣的話一旦數(shù)據(jù)發(fā)生改變的時(shí)候,就反饋到頁(yè)面上面去。代碼如下:
// state_name:klivitam
<template>
<div>
{{username}}
</div>
</template>
<script>
export default {
computed:{
username(){
return this.$store.state.name;
}
}
}
</script>
state總結(jié):用來(lái)存放組件之間共享的數(shù)據(jù)。他跟組件的data選項(xiàng)類似,只不過(guò)data選項(xiàng)是用來(lái)存放組件的私有數(shù)據(jù)。
2.2、 getters:
其次是getters,來(lái)看下面一段代碼:
vuex里面的代碼
import Vue from "vue";
import Vuex from "vuex"
let state = {
name:"klivitam"
}
let getters = {
getName(state){
return `我的名字是:${state.name}`;
}
}
let mutations = {}
let actions = {}
Vue.use(Vuex)
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
vue內(nèi)的代碼:
<template>
<div>
{{username}}
</div>
</template>
<script>
export default {
data(){
return{
message: '我是一個(gè)菜鳥(niǎo)',
}
},
computed:{
username(){
return this.$store.getters.getName;
}
}
}
</script>
chrome頁(yè)面的效果圖:

getters總結(jié): getters主要是用來(lái)過(guò)濾和重組。這些事件最好也是能在計(jì)算屬性中完成,用于監(jiān)聽(tīng)事件變化的。
2.3、mutations:
來(lái)看下面一段代碼:
vue代碼:
<template>
<div>
{{username}}
<div>
<button @click="change">更改我的昵稱</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
message: '我是一個(gè)菜鳥(niǎo)',
}
},
methods:{
change(){
this.$store.commit("changeName","jkb...")
}
},
computed: {
username() {
return this.$store.getters.getName;
}
}
}
</script>
store代碼:
let mutations = {
changeName(state,name){
state.name = name;
}
}
點(diǎn)擊后效果:

mutations總結(jié):在 Vuex store 中,實(shí)際改變 狀態(tài)(state) 的唯一方式是通過(guò) 提交(commit) 一個(gè) mutation。mutations下的函數(shù)接收state作為參數(shù),兩一個(gè)參數(shù)叫做payload(載荷),payload是用來(lái)記錄開(kāi)發(fā)者使用該函數(shù)的一些信息,比如說(shuō)提交了什么,提交的東西是用來(lái)干什么的,包含多個(gè)字段,所以載荷一般是對(duì)象(其實(shí)這個(gè)東西跟git的commit很類似)還有一點(diǎn)需要注意:mutations方法必須是同步方法!
2.4、 actions:
來(lái)看下面一段代碼:
vue的代碼:
change(){
this.$store.dispatch("changeName","jkb...")
}
store的代碼:
let mutations = {
changeName(state,name){
state.name = name;
}
}
let actions = {
changeName({commit},name){
let timer = setTimeout(()=>{
commit("changeName",name);
clearTimeout(timer);
},1000)
}
}
actions總結(jié):actions的作用其實(shí)和mutations是沒(méi)有差別的,無(wú)非就是一個(gè)同步、異步的差別罷了。而在功能上面主要有一下兩個(gè)區(qū)別:
- actions 提交的是 mutations,而不是直接變更狀態(tài)。也就是說(shuō),actions會(huì)通過(guò)mutations,讓mutations幫他提交數(shù)據(jù)的變更。
- actions 可以包含任意異步操作。ajax、setTimeout、setInterval不在話下。
最簡(jiǎn)單的使用,其實(shí)也就這些 但是很可能會(huì)存在很多深沉次的思考,接下來(lái) 我就趁熱打鐵,寫(xiě)一寫(xiě)vuex深沉次的思考。
三、 vuex的深層次思考
關(guān)于深層次的思考,我也做了這么久了。我其實(shí)主要的考量有兩個(gè)地方:
- 當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)時(shí)候,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余。
- 當(dāng)頁(yè)面的屬性過(guò)多的時(shí)候,代碼的可維護(hù)性下降。
針對(duì)上面的情況,我從這倆個(gè)切入點(diǎn) 說(shuō)說(shuō)我的想法:
1、 頁(yè)面計(jì)算屬性重復(fù)和冗余問(wèn)題:
關(guān)于頁(yè)面計(jì)算屬性重復(fù)和冗余的問(wèn)題,其實(shí)官方存在一個(gè)map函數(shù)來(lái)解決這個(gè)問(wèn)題。來(lái)看下面一段代碼。
import {mapState,mapGetters} from "vuex"
computed: {
...mapState([
"name",
"type"
]),
...mapGetters([
"getName"
])
}
這種做法也能達(dá)到效果。當(dāng)然也可以進(jìn)行重新命名的,例如這樣:
...mapState({
name:state=>state.name,
type:state=>state.type
})
2、如何按功能模塊分state屬性:
當(dāng)我們項(xiàng)目開(kāi)始復(fù)雜起來(lái)的時(shí)候,我們發(fā)現(xiàn)單個(gè)的vuex文件已經(jīng)無(wú)法去滿足我們的眼球了,當(dāng)然也是能夠進(jìn)行維護(hù)的,只是維護(hù)起來(lái)的成本開(kāi)銷(xiāo)比較大。此時(shí)就需要引入一個(gè)新的概念:Module. module主要的功能是將大型的store分成一個(gè)個(gè)模塊。
來(lái)看下面一段代碼:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
moduleA,
moduleB
}
})
這樣來(lái)獲取的時(shí)候:
...mapState({
a: state => state.moduleA.a,
})
這里有一個(gè)問(wèn)題,就是在派發(fā)(dispatch)的時(shí)候,是沒(méi)有做任何的區(qū)別的,所以很可能會(huì)遇到兩個(gè)模塊的actions事件名字是一樣的。我在這里呢?看到了 一個(gè)新的解決辦法--命名空間。來(lái)看下面一段代碼:
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模塊內(nèi)容(module assets)
state: { ... }, // 模塊內(nèi)的狀態(tài)已經(jīng)是嵌套的了,使用 `namespaced` 屬性不會(huì)對(duì)其產(chǎn)生影響
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模塊
modules: {
// 繼承父模塊的命名空間
myPage: {
state: { ... },
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 進(jìn)一步嵌套命名空間
posts: {
namespaced: true,
state: { ... },
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
當(dāng)然modules里面還有很多深層次的使用方法,這就需要你去做更深層次的研究了,例如在帶命名空間的模塊內(nèi)訪問(wèn)全局內(nèi)容、在帶命名空間的模塊注冊(cè)全局 action...等等。在實(shí)際的開(kāi)發(fā)中其實(shí)會(huì)用到很多的 這就需要去讀者朋友更深層次的進(jìn)行學(xué)習(xí)和使用了。
四、 vuex的封裝
前面說(shuō)了這么多,又到了愉快的封裝時(shí)間了。
store/index.js:
import vuex from 'vuex'
import aModule from "./a"
...modules
import vue from 'vue'
vue.use(vuex)
export default new vuex.Store({
modules: {
aModule
...modules
}
})
store/aModule/types.js //主要用來(lái)封裝事件名
export default {
ACTIONS_NAME: "ACTIONS_NAME"
}
store/aModule/actions.js //主要用來(lái)處理邏輯、注冊(cè)事件
import types from "./types";
export default {
actions ({ commit }, data) {
commit(types.ACTIONS_NAME, data)
}
}
store/aModule/mutations.js //主要用來(lái)修改/保存狀態(tài)
import types from "./types"
export default {
[types.ACTIONS_NAME](state, data) {
// set state
}
}
store/aModule/index.js
import actions from "./actions";
import mutations from "./mutations"
const state = {};
export default {
namespaced: true,
state:state,
mutations:mutations,
actions:actions,
getters:{
}
}
說(shuō)在最后
其實(shí)vuex真的是一個(gè)很不錯(cuò)的插件,也比較契合我們的開(kāi)發(fā)習(xí)慣。當(dāng)然很多人還是習(xí)慣用上面那幾種方式來(lái)維護(hù)代碼,但是我真的建議所有的程序員都在看看這個(gè)插件,學(xué)起來(lái)很簡(jiǎn)單 用起來(lái)也特別簡(jiǎn)單 最近在開(kāi)發(fā)小程序之余(公司需求,沒(méi)有用mpvue,用原生開(kāi)發(fā)),也去研究了一下vuex的源碼,如果有所得,還是會(huì)分享給大家。