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