看完了官網vuex一頭霧水的,比如vuex中的...mapState、countAlias: 'count'等。沒看過官網也沒事,希望對你們有幫助。
下文都是我的個人理解,官網的就引用了代碼,也許存在著很多錯誤的描述或者理解,請各位請教,我會改正的。
這個坑之前就深挖了~現在是時候填了。
為什么要有vuex?
它是管理組件與組件通信的庫。
子組件與父組件之間通信還是很方便的。但是如果是同級組件,或者是其它的復雜情況,比如:
- a.vue中的子組件想與b.vue中的子組件的通迅
- a.vue想與b.vue中的子組件的通迅
- a.vue中的子組件想與b.vue通迅
- a.vue的子組件的子組件想與b.vue的子組件通迅
- .....
就是為了解決了類似的問題~
我們把事件想簡單點吧,如果你管理那么多組件(這個改變了那個的數據,那個改變了這個的數據,那個組件又多了點什么,這個又少了點什么),你會怎么做呢?就說想法,不用實現。
我的想法是:沒有蛀牙 把整個應用的數據全部集中起來,誰想用作顯示都無條件同意,而誰想改,改完了一定要通知所以用到這個數據顯示的組件,還不是美滋滋?
vuex也是這個意思,它一共分為以下5個部分
- state 放數據的
- Getter 獲取數據的
- Mutation 同步改變數據的
- Action 異步改變數據(利用Mutation實現)
- Module 可以把全局的數據倉庫分為幾個單獨的模塊,比如用戶一個,文章一個。
1 state
1.1 state 定義
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
然后mutations先提一下怎么用吧(官網的例子,原汁原味 )
store.commit('increment')
console.log(store.state.count) // -> 1
然后要把這貨放在組件里,如果放入父組件中,那么子組件也可以使用。通過 this.$store。
//普通的定義
const app = new Vue({
el: '#app',
// 把 store 對象提供給 “store” 選項,這可以把 store 的實例注入所有的子組件
store,
components: {XXX},
template: `XXX`
})
//*.vue
export default {
el: '#app',
store,
components: {XXX}
}
</script>
1.2 關于mapState
mapState的作用說到底,就是兩個作用,先說第一個:別名。
上官網的例子,真的是要了親命了唉):
// 在單獨構建的版本中輔助函數為 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭頭函數可使代碼更簡練
count: state => state.count,
// 傳字符串參數 'count' 等同于 `state => state.count`
countAlias: 'count',
// 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
先說說mapState的作用,返回一個對象。
什么樣的對象
{
cout: xxx //第一個
coutAlias:xxx //第二個
countPlusLocalState(state){xxxx}//第三個
}
我盡可能說詳細一點,
export default {
computed: mapState({
/*根據箭頭函數,生成的函數為
function(state){ return state.count},
所以count的值最后是state.count。count是個值。*/
count: state => state.count,
/* countAlias只是個名字,與上面的count沒有一毛錢關系,我們的大前提,
是使用的vuex里的mapstate,所以,只要我們給一個store里的屬性名,
mapstate就會把屬性名對應的屬性返回給我們,
并用我們提供的新名字。所以,countAliae就對應的state里的count。
*/
countAlias: 'count',
/*注意,我強調一下,如果你只給屬性名,不給別名,那么就會按原屬性名返回。 比如就是一個孤零零的 'a' 你調用的時候也要調a就完事了,上面的是調countAlias */
// 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數
/* 箭頭函數基礎,親,如果你看不懂為什么要這樣,請去參考下ES6的箭頭函數 */
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
接下來講mapState的第二個作用:偷懶。
突然出現了一個...mapState,這個...就是對象展開運算符,是ES6里的,可關鍵是為什么搞了這么一個另人費解的東西,有什么用呢?
答:并沒有什么卵用還是有點用的,比如,你的compute屬性里想用store里返回的東西,一共用5個!就可以用mapstate傳5個屬性名進去把五個搞出來再添加到compute里。
比如
computed:{
a1:function(){xxxxx}
}
可是你需要a2到a6,如果一個一個的寫的話,就會這樣:
computed:{
a1:function(){xxxxx},
a2:function(){return this.$store.a2},
a3:function(){return this.$store.a3},
.......
}
有了這個...
//注意,我再強調一下,如果你只給屬性名,不給別名,
那么就會按原屬性名返回。所以可以這么寫:
compute:{
a1:function(){xxxxx},
...mapState({'a2','a3','a4','a5','a6'})
}
然后就全有了,和上面的作用一樣,看吧,偷懶就完事了。
2 Getter
首先要審明下,做為讀書人的我,也是會罵人的!?。?!
之前很多程序員總是有些很奇怪的愛好,比如他母親的Getter。這個Getter一見到名字就是想起了被java支配的恐懼,所以不說了 所以一定要好好理解!
vuex里的getter,是在store里的一個東西,為什么需要它呢?比如,
this.$store.state.todos.filter(todo => todo.done).length
這一句分別在十個函數里都出現了,就可以把他考慮做成一個getter函數。
把官網的例子搞了過來
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)
}
}
})
Getter 會暴露為 store.getters 對象:
store.getters.doneTodos // 運行會得到 [{ id: 1, text: '...', done: true }]
Getter接受其他 getter 作為第二個參數,很奇怪是吧,但是有時候需要用到getters里的別的函數。(這里定義時不支持this直接拿)
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // 得到1
在任何組件中(只要是上一級組件有store的)都可以使用:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
getter 為什么好好的要返回一個函數?因為你返回了一個函數,還可以給函數傳值,讓他查些什么玩意,難道你們沒有發現上面的例子都只是沒有傳我們自定義的參數的?現在發現了就行~
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
還有剩下的mapGetters,嘿嘿,和上面的mapState一樣,返回屬性的規則也一樣:1 別名 2 偷懶
3 Mutation
如果 1與2都是獲取數據展示的話,那么3與4就是修改完通知了~
搞一手官網:
更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,并且它會接受 state 作為第一個參數。
一個最基本的例子:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 變更狀態
state.count++
}
}
})
//使用:
store.commit('increment')
//注意,這里的increment 只能通過store.commit('increment')才能觸發
載荷,傳事件的時候同時傳個參數
既然能傳一個字符串的事件類型的increment,那么一定會有別的更復雜的使用方法。妥妥的,commit里還能搞個別的東西傳過去,這個就叫:載荷(payload)(我覺得可以理解成: 裝備)
mutations: {
increment (state, payload) {
state.count += payload.amount
}
//提交的風格有兩種(還是從官網上搞的)
//風格1:
store.commit('increment', {
amount: 10
})
//風格2:(個人覺得這個逼格更高一點點)
store.commit({
type: 'increment',
amount: 10
})
使用payload也是有條件的,
- 提前在你的 store 中初始化好所有所需屬性
- 在對象上添加新屬性時,應該更新這個屬性,而不是再用一個新的對象
- 使用
Vue.set(obj, 'newProp', 123)
- 以新對象替換老對象。
state.obj = { ...state.obj, newProp: 123 }
- 使用
使用更短的名字使用Mutation&在組件中使用Mutation
你可以在組件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用(需要在根節點注入 store)。 (官網原話)
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', /*將 `this.increment()` 映射為
`this.$store.commit('increment')`*/
// `mapMutations` 也支持載荷:
'incrementBy' /*將 `this.incrementBy(amount)` 映射為
`this.$store.commit('incrementBy', amount)`*/
]),
...mapMutations({
add: 'increment' /* 將 `this.add()` 映射為
`this.$store.commit('increment')`*/
})
}
}
然后官網也提到了使用mutations的注意事項
不能使用異步函數(等待時間不能太長的函數,什么網絡連接,讀寫文件,都不是異步函數)
如果你的程序中,有很多'increment'這種事件,推薦用常量保存后放在一個文件中,就像這樣:
//文件名: mutation-types.js
export const GO_TO_PICS = 'GO_TO_PICS'
export const GO_TO_NEWS = 'GO_TO_NEWS'
export const GO_TO_WE = 'GO_TO_WE'
4 Action
離4這個標題不遠處,你依稀可以看到,mutations不能使用異步函數,
Action就是提交異步事件的~
Action 提交的是 mutation,Action提交的是Action ,提交的是 mutation。重要的事件要說三遍。所以,Action是在mutation外又加了一層,看一個簡單的例子。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
context是什么玩意?有什么用?
答:他是和 store 實例具有相同方法和屬性的 context 對象。作用是[個人猜測,未驗證]context對象作為store的輔助,也許是store的一些操作一定要通過context進行。
調用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。
少寫一點(引用自網站,純語法):
實踐中,我們會經常用到 ES2015 的 參數解構 來簡化代碼(特別是我們需要調用
commit
很多次的時候):
actions: {
//這是ES6的語法
/*舉個例子:{ blowUp } = { blowUp: 10 }; 結果:{blowUp:10}
從blowUp中取一個叫blowUp的屬性。
參數本來是傳過來的context,從context中取一個commit的東西,得到 { commit : context.commit };
挺另人頭大的。
*/
increment ({ commit }) {
commit('increment')
}
}
使用action
store.dispatch('increment')
但是最主要是異步調用:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
同樣的支持載荷(payload,裝備)
// 以載荷形式分發
store.dispatch('incrementAsync', {
amount: 10
})
// 以對象形式分發
store.dispatch({
type: 'incrementAsync',
amount: 10
})
官網上的一個更復雜的例子-購物車:
actions: {
// es6的語法,從context中取commit與state
checkout ({ commit, state }, products) {
// 把當前購物車的物品備份起來 ...對象展開
const savedCartItems = [...state.cart.added]
// 發出結賬請求,然后樂觀地清空購物車
commit(types.CHECKOUT_REQUEST)
// 購物 API 接受一個成功回調和一個失敗回調
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失敗操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
上面只是定義,還有在組件中使用action的方法,不要忘記這個。
關于mapActions,作用和定義你們懂得,下面用的官網的例子:
你在組件中使用
this.$store.dispatch('xxx')
分發 action,或者使用mapActions
輔助函數將組件的 methods 映射為store.dispatch
調用(需要先在根節點注入store
):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', /* 將 `this.increment()` 映射為
`this.$store.dispatch('increment')`*/
// `mapActions` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`
})
}
}
組合 Action(全盤搬的官網,這個部分很好理解)
Action 通常是異步的,那么如何知道 action 什么時候結束呢?更重要的是,我們如何才能組合多個 action,以處理更加復雜的異步流程?
首先,你需要明白 store.dispatch
可以處理被觸發的 action 的處理函數返回的 Promise,并且 store.dispatch
仍舊返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
現在你可以:
store.dispatch('actionA').then(() => {
// ...
})
在另外一個 action 中也可以:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
最后,如果我們利用 async / await,我們可以如下組合 action:
// 假設 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
一個
store.dispatch
在不同模塊中可以觸發多個 action 函數。在這種情況下,只有當所有觸發函數完成后,返回的 Promise 才會執行。
5 Module
這個部分以后再說吧,想了解的可以去官網看看先。