參考來源:
Vex 是什么?
vuex 是一個專為 vue 應(yīng)用程序開發(fā)的 狀態(tài)管理模式。保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。
Vuex 作用一覽:

簡單的例子??:
// Vue.use(Vuex)
const Store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment (state) {
state.count ++
}
}
})
// 使用
store.commit('increment');
console.log(store.state.count); // -> 1
約定通過提交 mutation 的方式,而非直接改變 store.state.count,更加明確追蹤到狀態(tài)的變化。
一個 Vuex 的 Store 分為四個部分:
- State
- Getter
- Mutation
- Action
State(單一狀態(tài)樹)
在vue中使用 State 狀態(tài)的最簡單的方法就是在計算屬性(computed)中返回某個狀態(tài)值:
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count() {
return this.$store.state.count
}
}
}
上述過程中,每當 store 中的 count 狀態(tài)值發(fā)生變化,computed 屬性都能監(jiān)聽得到,并且觸發(fā)相關(guān)聯(lián)的 DOM的更新。
mapState 輔助函數(shù)
當一個組件需要獲取多個狀態(tài)值,將這些狀態(tài)都聲明在計算屬性中會顯得臃腫不堪,Vuex 提供了一個 mapState 函數(shù)簡化這一操作:
import { mapState } from 'vuex';
export default {
//...
computed: {
otherComputed() {},
// ...
...mapState({
count: state => state.count,
// 傳字符串參數(shù) 'count' 等同于 `state => state.count`
countAlias: 'count',
// 為了能夠使用 `this` 獲取局部狀態(tài),必須使用常規(guī)函數(shù)
countPlusLocalState(state) {
return state.count + this.localCount
}
})
}
}
Getter
有時候我們需要從 Store 中的 State 派生出一些狀態(tài),例如對列表進行過濾并記數(shù):
computed: {
doneTodosCount() {
return this.$store.store.todos.filter(todo => todo.done).length
}
}
如果有多個組件需要用這個屬性,那么就需要在每個組件中都寫這么長的表達式,難免有些尷尬??。
這時候 Getter 就派上了用處(可以認為是 Store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據(jù)它的依賴被緩存起來,依賴變了,返回值也隨之改變。
const store = new Vuex.store({
state: {
todos: [
{ id: 1, text: '...', done: true},
{ id: 1, text: '...', done: false},
],
},
getters: {
doneTodos: state => state.todo.filter(todo => todo.done)
}
})
在組件中通過使用 store.getters 對象,可以以屬性的形式方為這些值:
store.getters.doneTodos
// -> [{ id: 1, text: '...', done: true }]
Getter也可以接受其他 getter 作為第二個參數(shù):
getters: {
// ...
doneTodosCount: (state, getters) => getters.doneTodos.length,
}
// 使用
store.getters.doneTodosCount // -> 1
通過方法訪問
可以對 getter 的返回值做些手腳:
getters: {
// ...
getTodosById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
// 使用
store.getters.getTodoById(2)
// -> { id: 2, text: '...', done: false }
使 getter 返回一個函數(shù)時,可以適應(yīng)許多常用場景。getter 在使用 方法訪問時,每次都會進行調(diào)用而不會緩存結(jié)果。
mapGetters 輔助函數(shù)
類似于 mapState ,vuex 也提供了 mapGetters 方便使用:
import { mapGetters } from 'vuex'
export default {
//...
computed: {
// 使用對象展開運算符將 getter 混入 computed對象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
//...
])
}
}
Mutation
更改 Vuex 的 store 的狀態(tài)的唯一方法就是提交 mutation。每個 mutation 都有字符串的 事件類型(type) 和一個 回調(diào)函數(shù)(handler):
const store = new Vuex.store({
state: {
count: 1,
},
mutation: {
icrement (state) {
state.count ++
},
decrement (state, { delta }) {
state.count -= delta
}
}
});
// 使用
store.commit('icrement');
store.commit('decrement', { delta: 10 });
// 對象風格的提交方式
store.commit({
type: 'decrement',
delta: 10,
})
Mutation 需遵守 Vue 的響應(yīng)規(guī)則:
提前初始化 store 中所有的屬性。
-
當需要在對象上添加新屬性時
使用
Vue.set(obj, 'newProp', 123)或者-
以新對象替換老對象,例如:
state.obj = { ...state.obj, newProp: 123 }
使用常量替代 Mutation 事件類型
使用常量替代 mutation 事件類型在各種 Flux 實現(xiàn)中是很常見的模式。這樣可以使 linter 之類的工具發(fā)揮作用,同時把這些常量放在單獨的文件中可以讓你的代碼合作者對整個 app 包含的 mutation 一目了然:
// mutation-types.js
export const SOME_MUTATION = 'SOME_'
使用類型常量:
// store.js
import Vuex from 'vuex';
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.store({
state: { /* ... */ },
mutations: {
// 使用 ES6 風格的常量作為函數(shù)名。
[SOME_MUTATION] (state) {
// mutate state
}
}
})
Mutation 必須是同步函數(shù) ??
任何回調(diào)函數(shù)中進行的狀態(tài)的改變都是不可追蹤的!在 mutation 中混合異步調(diào)用會導(dǎo)致你的程序很難調(diào)試。
在組件中提交 Mutation
在組件中使用
this.$store.commit('xxx')提交 mutation;-
使用
mapMutation輔助函數(shù)將 mutation 方法混入組件的 methods 中:import { mapMutatioins } from 'Vuex'; export default { // other props 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')` }) } }
Action
Action 類似于 Mutation,不同于:
- Action 提交的是 mutation,而不是直接變更狀態(tài);
- Action 可以包含任意異步操作;
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
},
// 參數(shù)結(jié)構(gòu):
decrement ({ commit }) {
commit('decrement')
}
}
});
Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象,因此可以調(diào)用 context.commit 方法提交一個 Mutation,或者使用 context.state 和 context.getters 獲取 state 和 getters。
??context 對象并不是 store 實例本身
分發(fā) Action
store.dispatch('increment');
與 Mutation 不一樣的是,在 Action 內(nèi)部是可以執(zhí)行 異步 操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
在組件中分發(fā) Action
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
store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch 仍舊返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
},
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
// 使用:
store.dispatch('actionB').then(() => {
// ...
})
以及比較復(fù)雜的使用:
// 假設(shè) getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
規(guī)則建議:
- 應(yīng)用層級的狀態(tài)應(yīng)該集中到單個 store 對象中。
- 提交 mutation 是更改狀態(tài)的唯一方法,這個過程是同步的。
- 異步邏輯都應(yīng)該封裝到 action 中。
相關(guān)面試題
?? 什么時候用 Vuex
- 多個組件依賴于同一狀態(tài)。
- 來自不同組件行為需要更改同一狀態(tài)。
?? Vuex 中狀態(tài)是對象時,使用時要注意什么?
對象時引用類型,復(fù)制后改變屬性還是會影響原始數(shù)據(jù),所以在賦值前,先試用DeepClone
?? 怎么在組件中批量使用Vuex的state狀態(tài)?
使用mapState輔助函數(shù), 利用對象展開運算符將state混入computed對象中
import {mapState} from 'vuex'
export default{
computed:{
...mapState(['price','number'])
}
}
?? Vuex中要從state派生一些狀態(tài)出來,且多個組件使用它,該怎么做?
使用 getter 屬性:
const store = new Vuex.store({
state: {
price: 10,
number: 10,
discount: 0.7,
},
getters: {
total: state => state.price * state.number,
discountTotal: (state, getters) => state.discount * getters.total,
},
})
面試問題待收錄