對于學(xué)習過react的同學(xué)可能比較清楚呻待,在react我們是通過redux來處理狀態(tài)管理的矾削,那么現(xiàn)在火熱的vue是如何做到管理頁面數(shù)據(jù)的呢萎津,答案就是vuex咧七。
1 什么情況下使用vuex
Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式廊营。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài)歪泳,如果您不打算開發(fā)大型單頁應(yīng)用,使用 Vuex 可能是繁瑣冗余的露筒。如果您需要構(gòu)建是一個中大型單頁應(yīng)用呐伞,您很可能會考慮如何更好地在組件外部管理狀態(tài),Vuex 將會成為自然而然的選擇慎式。
下面展示一個單向數(shù)據(jù)流的圖片:
頁面中模版獲取state數(shù)據(jù)渲染頁面伶氢,用戶通過action改變數(shù)據(jù)導(dǎo)致頁面重新渲染。
2 vuex Store
每一個 Vuex 應(yīng)用的核心就是 store(倉庫)瘪吏。"store" 基本上就是一個容器癣防,它包含著你的應(yīng)用中大部分的狀態(tài)(state)。Vuex 和單純的全局對象有以下兩點不同:
Vuex 的狀態(tài)存儲是響應(yīng)式的掌眠。當 Vue 組件從 store 中讀取狀態(tài)的時候蕾盯,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會相應(yīng)地得到高效更新蓝丙。
你不能直接改變 store 中的狀態(tài)级遭。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交(commit) mutations望拖。這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用挫鸽。
const store = new Vuex.Store({
state: {
count: 0,
step:2
},
mutations: {
increment (state) {
state.count += state.step
}
}
})
現(xiàn)在说敏,你可以通過 store.state 來獲取狀態(tài)對象,以及通過 store.commit 方法觸發(fā)狀態(tài)變更:
store.commit('increment')
console.log(store.state.count) // -> 2
3vuex State
Vuex 使用 單一狀態(tài)樹 —— 是的丢郊,用一個對象就包含了全部的應(yīng)用層級狀態(tài)盔沫。至此它便作為一個『唯一數(shù)據(jù)源(SSOT)』而存在。這也意味著枫匾,每個應(yīng)用將僅僅包含一個 store 實例架诞。
// 創(chuàng)建一個 Counter 組件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
我們在這個組件里面用到了count,當我們通過store.commit('increment')時婿牍,組件里會發(fā)生rerender。
計算屬性示例:
<div id="example">
<p>my score grade: "{{ level }}"</p>
<p>i get "{{ score }}" point in exam</p>
<button @click="change()">add</button>
</div>
var vm = new Vue({
el: '#example',
data: {
score: '87'
},
computed: {
level: function () {
return this.score > 60 ? '合格' :'不合格'
}
},
method:{
change:function(){
this.score += 5
}
}
})
我們可以看到level是依賴于score的惩歉,如果我們通過點擊button方法等脂,會同是重新計算level的值。
Vuex 通過 store 選項撑蚌,提供了一種機制將狀態(tài)從根組件『注入』到每一個子組件中(需調(diào)用 Vue.use(Vuex)):
const app = new Vue({
el: '#app',
// 把 store 對象提供給 “store” 選項上遥,這可以把 store 的實例注入所有的子組件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
4 mapState 輔助函數(shù)
當一個組件需要獲取多個狀態(tài)時候,將這些狀態(tài)都聲明為計算屬性會有些重復(fù)和冗余争涌。為了解決這個問題粉楚,我們可以使用 mapState 輔助函數(shù)幫助我們生成計算屬性,讓你少按幾次鍵:
// 在單獨構(gòu)建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'
const store = new Vuex.store({
state:{
apple:5,
orange:2
}
})
export default {
computed: mapState({
// 箭頭函數(shù)可使代碼更簡練
apple: state => state.apple,
total (state) {
return state.apple + state.orange
}
})
}
上面這個例子顯然易見亮垫,我們把store中的數(shù)據(jù)放到了頁面上模软。
當映射的計算屬性的名稱與 state 的子節(jié)點名稱相同時,我們也可以給 mapState 傳一個字符串數(shù)組饮潦。
computed: mapState([
// 映射 this.count 為 store.state.count
'apple燃异,orange'
])
有了對象擴展符,我們可以這樣寫:
computed: {
attribute1:function(){
},
...mapState([apple,orange])
}
5 vuex Getter
Vuex 允許我們在 store 中定義『getters』(可以認為是 store 的計算屬性)继蜡。Getters 接受 state 作為其第一個參數(shù):
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
Getters 會暴露為 store.getters 對象:
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getters 也可以接受其他 getters 作為第二個參數(shù):
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
我們可以很容易地在任何組件中使用它:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
getters的神奇之處就是參數(shù)可以有2個回俐,state和getters,所以這就有無限可能稀并。
6 mapGetters 輔助函數(shù)
mapGetters 輔助函數(shù)僅僅是將 store 中的 getters 映射到局部計算屬性:
import { mapGetters } from 'vuex'
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false },
{ id: 3, text: '...', done: true },
],
another:2
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneCount:(state,getter){
return state.another + getters.doneCounts.length
}
}
})
export default {
computed: {
...mapGetters([
'doneTodosCount',
'doneCount',
// ...
])
}
}
如果你想將一個 getter 屬性另取一個名字仅颇,使用對象形式:
mapGetters({
// 映射 this.doneCount 為 store.getters.doneTodosCount
doneCount: 'doneTodosCount'
})
7 Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutations 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)碘举。這個回調(diào)函數(shù)就是我們實際進行狀態(tài)更改的地方忘瓦,并且它會接受 state 作為第一個參數(shù):
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state,n) {
// 變更狀態(tài)
state.count += n
}
}
})
store.commit('increment', 10)
其實我還可以傳對象。
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
對象風格的提交方式
store.commit({
type: 'increment',
amount: 10
})
當使用對象風格的提交方式引颈,整個對象都作為載荷傳給 mutation 函數(shù)政冻,因此 handler 保持不變
接下來知道我要講什么嘛枚抵,沒錯就是map了。明场。汽摹。
import { mapMutations } from 'vuex'
computed: {
...mapGetters([
'doneTodosCount',
'count',
])
},
methods: {
...mapMutations([
'increment','decrement',
]),
change(){
this.increment() == this.store.commit('increment')
this.doneTodosCount += this.count
}
注意執(zhí)行了increment后count的值發(fā)生了變化。
8 vuex Action
Action 類似于 mutation苦锨,不同在于:
- Action 提交的是 mutation逼泣,而不是直接變更狀態(tài)。
- Action 可以包含任意異步操作舟舒。
讓我們來注冊一個簡單的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment ({commit}) {
commit('increment')
}
}
})
乍一眼看上去感覺多此一舉拉庶,我們直接分發(fā) mutation 豈不更方便?實際上并非如此秃励,還記得 mutation 必須同步執(zhí)行這個限制么氏仗?Action 就不受約束!我們可以在 action 內(nèi)部執(zhí)行異步操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
通過store.dispatch('increment')分發(fā)夺鲜。舉個例子說吧
import * as types from './mutation-types'
const store = new vex.store({
mutation:{
addcart(state, product){
state.count += product.num
}
},
actions: {
addToCart ({ commit, state }, product) => {
if (product.inventory > 0) {
commit(types.ADD_TO_CART, {
num: product.num
})
}
}
可以用store.dispatch('addToCart',{num:3,name:'fruit'})來分發(fā)皆尔。
當然這個也有map用法,和前面講的一樣币励。
組合 Actions
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch('actionA').then(() => {
// ...
})
對promise不夠了解的可以去學(xué)一下慷蠕。
在另外一個 action 中也可以:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
好了,今天講的東西有點多食呻,下回我會結(jié)合實際項目詳解vuex的用法流炕。