在
SPA
單頁面組件的開發中Vue
的Vuex
和React
的Redux
都統稱爲同一狀態管理
個人的理解是全局狀態管理更合適
簡單的理解就是你在state
中定義了一個數據之後,你可以在所在項目中的任何一個組件裏進行獲取、進行修改,並且你的修改可以得到全局的響應變更
什麼是Vuex?
官方說法:
Vuex
是一個專爲Vue.js
應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
個人理解:
Vuex
是用來管理組件之間通信的一個插件
爲什麼要用Vuex?
我們知道組件之間是獨立的,組件之間想要實現通信
我目前知道的就只有props
選項,但這也僅限於父組件和 子組件之間的通信
如果兄弟組件之間想要實現通信呢?嗯…,方法也有,但是實現就比較麻煩了
這和現代框架理論(讓我們程序猿把重心都放在業務邏輯的實現上)相違背
拋開怎麼實現的問題,試想一下,當做中大型項目時,面對一大堆組件之間的通信,還有一大堆的邏輯代碼,會不會很抓狂??
那爲何不把組件之間共享的數據給“拎”出來,在一定的規則下管理這些數據呢?
這就是Vuex的產生的意義和基本思想了
怎麼使用Vuex?
話不多說,我們先上代碼!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<script src="./js/vuex.js"></script>
<script src="./js/vue2.0.js"></script>
<body>
<div id="app">
</div>
</body>
<script>
//在創建Vue實例之前
Vue.use(Vuex);
var myStore = new Vuex.Store({
state:{
//存放組件之間共享的數據
name:"jjk"
},
getters:{
//獲取數據的方法
},
mutations:{
//顯式的更改state裏的數據
},
actions:{
//
}
});
new Vue({
el:"#app",
data:{
name:"dk"
},
// 註冊store,到vue實例上
store:myStore,
mounted:function(){
console.log(this)//控制檯
}
})
</script>
</html>
先解釋上面代碼的意思:
new Vuex.Store({})
表示創建一個Vuex實例,通常情況下,他需要注入到Vue實例裏
Store是Vuex的一個核心方法,字面上理解爲“倉庫”的意思。
Vuex Store是響應式的,當Vue組件從store中讀取狀態(state選項)時,
若store中的狀態發生更新時,他會及時的響應給其他的組件(類似雙向數據綁定),
而且不能直接改變store的狀態,
改變狀態的唯一方法就是,顯式地提交更改(mutations選項)
- 那如何獲取到state的數據呢?
一般會在組件的計算屬性(
computed
)獲取state
的數據
因爲,計算屬性
會監控數據變化,一旦發生改變就會響應(重新計算)
// 在剛剛的頁面,我們再創建一個全局組件
Vue.component('hello',{
template:"<p>{{name}}</p>",
computed: {
name:function(){
return this.$store.state.name
}
},
mounted:function(){
console.log(this)
}
})
Vuex
有5個核心選項:- state
- getters
- mutations
- actions
- modules
state
:用來存放組件之間共享的數據。他跟組件的data選項類似,只不過data選項是用來存放組件的私有數據。
getters
:有時候,我們需要對state的數據進行篩選、過濾。
這些操作都是在組件的計算屬性進行的,如果多個組件需要用到篩選後的數據,那我們就必須到處重複寫該計算屬性函數;或者將其提取到一個公共的工具函數中,並將公共函數多處導入 - 兩者都不太理想。
如果把數據篩選完,再傳到計算屬性裏就不用那麼麻煩了,getters就是幹這個的
你可以把getters看成是store的計算屬性
getters下的函數接收接收state作爲第一個參數
如果getters對象有相應的方法,組件可以通過$store.getters對象.方法
來獲取數據
mutations
:前面講到的都是如何獲取state的數據,那如何把數據存儲到state中呢?在 Vuex store 中,實際改變 狀態(state) 的唯一方式是通過 提交(commit) 一個mutation
mutations下的函數接收state作爲參數,接收第二個參數payload(載荷),第二個參數是用來記錄開發者使用該函數的一些信息,比如說提交了什麼,提交的東西是用來幹什麼的,包含多個字段,所以載荷一般是對象(其實這個東西跟git的commit很類似)
還有一點需要注意:mutations方法必須是同步方法!
actions
:既然mutations只能處理同步函數,我大js全靠‘異步回調’吃飯,怎麼能沒有異步,於是actions出現了…
-
actions
和mutations
的區別Actions 提交的是 mutations,而不是直接變更狀態。 也就是說,actions會通過mutations,讓mutations幫他提交數據的變更。 Action 可以包含任意異步操作。ajax、setTimeout、setInterval不在話下
modules
: 因爲在大多數的項目中,我們對於全局狀態的管理並不僅僅一種情況的需求,有時有多方面的需求,比如寫一個商城項目,你所用到的全局state可能是關於購物車這一塊兒的也有可能是關於商品價格這一塊兒的;像這樣的情況我們就要考慮使用vuex中的 modules 模塊化了
具體怎麼使用modules呢?咱們在下面用vue-cli
構建的小呆萌裏在仔細講
- 總結:
各個類型的 API各司其職
mutation 只管存,你給我(dispatch)我就存
action只管中間處理,處理完我就給你,你怎麼存我不管
getter 我只管取,我不改的
action放在了 methods 裏面,說明我們應該把它當成函數來用(講道理,鉤子函數也應該可以的)
mutation是寫在store裏面的,這說明,它就是個半成品,中間量,我們不應該在外面去操作它。
getter寫在了 computed 裏面,這說明雖然 getter我們寫的是函數,但是我們應該把它當成計算屬性來用
上面都是對Vuex
的基礎入門理論介紹比較多,下面我們使用vue-cli
構建一個小呆萌,玩一玩 Vuex
使用 vue-cli 構建項目,並使用 Vuex
下面咱們一步一步地剖析下vuex的使用:
首先要安裝、使用 vuex
首先在 vue 2.0+ 你的vue-cli項目中安裝 vuex :
npm install vuex --save
然後 在src文件目錄下新建一個名爲store的文件夾,爲方便引入並在store文件夾裏新建一個index.js,裏面的內容如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store();
export default store;
接下來,在 main.js裏面引入store,然後再全局注入一下,這樣一來就可以在任何一個組件裏面使用this.$store了:
import store from './store'//引入store
new Vue({
el: '#app',
router,
store,//使用store
template: '<App/>',
components: { App }
})
說了上面的前奏之後,接下來就是納入正題了,就是開篇說的state的玩法。回到store文件的index.js裏面,我們先聲明一個state變量,並賦值一個空對象給它,裏面隨便定義兩個初始屬性值;然後再在實例化的Vuex.Store裏面傳入一個空對象,並把剛聲明的變量state仍裏面:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={//要設置的全局訪問的state對象
showFooter: true,
changableNum:0
//要設置的初始屬性值
};
const store = new Vuex.Store({
state
});
export default store;
實際上做完上面的三個步驟後,你已經可以用this.store.state.changebleNum在任何一個組件裏面獲取showfooter和changebleNum定義的值了,但這不是理想的獲取方式;vuex官方API提供了一個getters,和vue計算屬性computed一樣,來實時監聽state值的變化(最新狀態),並把它也仍進Vuex.Store裏面,具體看下面代碼:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要設置的全局訪問的state對象
showFooter: true,
changableNum:0
//要設置的初始屬性值
};
const getters = { //實時監聽state值的變化(最新狀態)
isShow(state) { //方法名隨意,主要是來承載變化的showFooter的值
return state.showFooter
},
getChangedNum(){ //方法名隨意,主要是用來承載變化的changableNum的值
return state.changebleNum
}
};
const store = new Vuex.Store({
state,
getters
});
export default store;
光有定義的state的初始值,不改變它不是我們想要的需求,接下來要說的就是mutations了,mutattions也是一個對象,這個對象裏面可以放改變state的初始值的方法,具體的用法就是給裏面的方法傳入參數state或額外的參數,然後利用vue的雙向數據驅動進行值的改變,同樣的定義好之後也把這個mutations扔進Vuex.Store裏面,如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要設置的全局訪問的state對象
showFooter: true,
changableNum:0
//要設置的初始屬性值
};
const getters = { //實時監聽state值的變化(最新狀態)
isShow(state) { //承載變化的showFooter的值
return state.showFooter
},
getChangedNum(){ //承載變化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) { //自定義改變state初始值的方法,這裏面的參數除了state之外還可以再傳額外的參數(變量或對象);
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上,這裏面的參數除了state之外還傳了需要增加的值sum
state.changableNum+=sum;
}
};
const store = new Vuex.Store({
state,
getters,
mutations
});
export default store;
這時候你完全可以用 this.store.commit(‘hide’) 以及 this.store.commit(‘newNum’,sum)方法,兩個組件裏用執行得到的值,每次都是一樣的,這樣肯定不是理想的需求
好在vuex官方API還提供了一個actions,這個actions也是個對象變量,最大的作用就是裏面的Action方法 可以包含任意異步操作,這裏面的方法是用來異步觸發mutations裏面的方法,actions裏面自定義的函數接收一個context參數和要變化的形參,context與store實例具有相同的方法和屬性,所以它可以執行context.commit(’ '),然後也不要忘了把它也扔進Vuex.Store裏面:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要設置的全局訪問的state對象
showFooter: true,
changableNum:0
//要設置的初始屬性值
};
const getters = { //實時監聽state值的變化(最新狀態)
isShow(state) { //承載變化的showFooter的值
return state.showFooter
},
getChangedNum(){ //承載變化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) { //自定義改變state初始值的方法,這裏面的參數除了state之外還可以再傳額外的參數(變量或對象);
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上,這裏面的參數除了state之外還傳了需要增加的值sum
state.changableNum+=sum;
}
};
const actions = {
hideFooter(context) { //自定義觸發mutations裏函數的方法,context與store 實例具有相同方法和屬性
context.commit('hide');
},
showFooter(context) { //同上註釋
context.commit('show');
},
getNewNum(context,num){ //同上註釋,num爲要變化的形參
context.commit('newNum',num)
}
};
const store = new Vuex.Store({
state,
getters,
mutations,
actions
});
export default store;
而在外部組件裏進行全局執行actions裏面方法的時候,你只需要用執行
this.$store.dispatch(‘hideFooter’)
或this.$store.dispatch(‘showFooter’)
以及this.$store.dispatch(‘getNewNum’,6) //6要變化的實參
這樣就可以全局改變改變showfooter或changebleNum的值了,如下面的組件中,需求是跳轉組件頁面後,根據當前所在的路由頁面進行隱藏或顯示頁面底部的tabs選項卡
<template>
<div id="app">
<router-view/>
<FooterBar v-if="isShow" />
</div>
</template>
<script>
import FooterBar from '@/components/common/FooterBar'
import config from './config/index'
export default {
name: 'App',
components:{
FooterBar:FooterBar
},
data(){
return {
}
},
computed:{
isShow(){
return this.$store.getters.isShow;
}
},
watch:{
$route(to,from){ //跳轉組件頁面後,監聽路由參數中對應的當前頁面以及上一個頁面
console.log(to)
if(to.name=='book'||to.name=='my'){ // to.name來獲取當前所顯示的頁面,從而控制該顯示或隱藏footerBar組件
this.$store.dispatch('showFooter') // 利用派發全局state.showFooter的值來控制
} else {
this.$store.dispatch('hideFooter')
}
}
}
}
</script>
至此就可以做到一呼百應的全局響應狀態改變了!