Vuex-一个专为 Vue.js 应用程序开发的状态管理模式
為什么會(huì)出現(xiàn)Vuex
非父子關(guān)系的組件如何進(jìn)行通信?(Event Bus)
bus.js
import Vue from 'vue';
export default new Vue(); foo.vue
import bus from './bus.js';
export default {methods: {changeBroData() {bus.$emit('changeBarData');}}
} bar.vue
import bus from './bus.js';
export default {created() {bus.$on('changeBarData',() => {this.count++;});}
} 查看效果
但是當(dāng)我們需要修改這個(gè)操作的時(shí)候,我們需要?jiǎng)?個(gè)地方,倘若項(xiàng)目小的話還倒好說,但是對于大項(xiàng)目組件間交互很多的概況,Event Bus就會(huì)表現(xiàn)的很吃力。Vuex的出現(xiàn)就是為了解決這一狀況。
Vuex介紹
安裝:
script標(biāo)簽引入https://unpkg.com/vuex
NPMnpm install vuex --save-dev
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex); 每一個(gè) Vuex 應(yīng)用的核心就是 store(倉庫)。“store”基本上就是一個(gè)容器,它包含著你的應(yīng)用中大部分的狀態(tài) (state)。Vuex 和單純的全局對象有以下兩點(diǎn)不同:
- Vuex的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng)Vuex的狀態(tài)屬性發(fā)生變化時(shí),相應(yīng)的組件也會(huì)更新。
- 無法直接修改Vuex的狀態(tài)。只能通過顯式的提交(commit)。
簡單的Store
const store = new Vuex.Store({state: {count: 0},mutations: {increase(state) {state.count++;}}
});
store.commit('increase');
console.log(store.state.count); // 1 State
從上文我們知道Vuex是響應(yīng)式的,我們?nèi)绾卧赩ue實(shí)例中使用Vuex中的實(shí)例呢,自然離不開計(jì)算屬性computed了。
在 Vue 組件中獲得 Vuex 狀態(tài)
//倉庫
const store = new Vuex.Store({state: {count: 1}
});
//foo組件
const foo = {template: `<div>{{ count }}<div>`,computed: {count: () => store.state.count}
}; 這時(shí)候問題就來了,如果這樣進(jìn)行引入store的話,組件就會(huì)以來全局狀態(tài)單例。
Vuex 通過 store 選項(xiàng),提供了一種機(jī)制將狀態(tài)從根組件“注入”到每一個(gè)子組件中(需調(diào)用 Vue.use(Vuex)):
//掛載App
new Vue({el: '#app',//注入store選項(xiàng)store,render: h => h(App)
});
//foo組件
const foo = {template: `<div>{{ count }}<div>`,computed: {count() {// 不能使用箭頭函數(shù),不然this就不是Vue實(shí)例了// 通過this.$store獲取到store實(shí)例return this.$store.state.count}}
}; 如果有很多狀態(tài)需要映射,我們豈不是要寫好多代碼,這時(shí)候需要用到mapState輔助函數(shù),使用 mapState 輔助函數(shù)幫助我們生成計(jì)算屬性。
輔助函數(shù) 實(shí)例1
const foo = {template: `<div>count: {{ count }}countAlias: {{ countAlias }}countPlusLocalCount: {{ countPlusLocalCount }}<div>`,data() {return {localCount: 2};},computed: Vuex.mapState({//只處理倉庫中的countcount: state => state.count*2,//映射countAlias: 'count',//需要用到Vue實(shí)例的話不能使用箭頭函數(shù)不然this無法獲取到Vue實(shí)例countPlusLocalCount(state) {return state.count + this.localCount;}})
}; 當(dāng)然當(dāng)映射名與state中屬性名相同時(shí),可以通過mapState(['count'])這種形式書寫。
因?yàn)榻M件本身也有許多計(jì)算屬性直接使用mapState的話無法擴(kuò)充computed了,這時(shí)候我們可以使用ES新屬性: 對象展開運(yùn)算符
computed: {localComputed() {return this.count;},...Vuex.mapState({//只處理倉庫中的countcount: state => state.count*2,//映射countAlias: 'count',//需要用到Vue實(shí)例的話不能使用箭頭函數(shù)不然this無法獲取到Vue實(shí)例countPlusLocalCount(state) {return state.count + this.localCount;}})
} ATT:使用 Vuex 并不意味著你需要將所有的狀態(tài)放入 Vuex。雖然將所有的狀態(tài)放到 Vuex 會(huì)使?fàn)顟B(tài)變化更顯式和易調(diào)試,但也會(huì)使代碼變得冗長和不直觀。如果有些狀態(tài)嚴(yán)格屬于單個(gè)組件,最好還是作為組件的局部狀態(tài)。你應(yīng)該根據(jù)你的應(yīng)用開發(fā)需要進(jìn)行權(quán)衡和確定。
Getter
有時(shí)候我們需要過濾/處理state中的屬性值,就像Vue實(shí)例中我們需要處理data數(shù)據(jù)一樣,Vue有computed。Vuex同樣提供給我們一個(gè)屬性getter,getter就是Vuex的“計(jì)算屬性”。
Getter接收state作為第一個(gè)參數(shù)
//倉庫
const store = new Vuex.Store({state: {users: [{name: 'jason',id: 1,female: false}, {name: 'molly',id: 2,female: true}, {name: 'steven',id: 3,female: false}]},getters: {// 過濾所有屬性中female是true的對象getFemaleUsers: state => state.users.filter(user => user.female)}
});
console.log(store.getters.getFemaleUsers);
//[{ "name": "molly", "id": 2, "female": true }] 當(dāng)然Getter同樣有輔助函數(shù) mapGetters將 store 中的 getter 映射到局部計(jì)算屬性
直接上對象展開運(yùn)算符了
輔助函數(shù) 實(shí)例1
//foo組件
const foo = {template: `<div>femaleList: {{ femaleList }}<div>`,computed: {// 屬性名相同的不再闡述...Vuex.mapGetters({femaleList: 'getFemaleUsers'})}
}; Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation
提交時(shí)額外的參數(shù)被稱為載荷
普通風(fēng)格提交方式
store.commit('mutationName', {params: '參數(shù)'
}); 對象風(fēng)格提交方式
store.commit({type: 'mutationName',params: '參數(shù)'
}); 注意點(diǎn):
- 設(shè)置對象新屬性時(shí)
方法1:Vue.set(obj, 'newVal', 100)
方法2:obj = {...obj, newVal: 100}; -
Mutations的事件類型盡量使用常量,并用一個(gè)文件進(jìn)行維護(hù)。方便協(xié)同開發(fā)以及維護(hù)。
``` // 存儲(chǔ)事件名mutations-types.js export const MUTATIONS_GETDATA = 'MUTATIONS_GETDATA'; export const MUTATIONS_SUCCESS = 'MUTATIONS_SUCCESS'; ``` ``` import { MUTATIONS_GETDATA ,MUTATIONS_SUCCESS } from 'path/mutations-types.js'; const store = new Vuex.Store({ mutations: { [MUTATIONS_GETDATA]() { // todo }, [MUTATIONS_SUCCESS]() { // todo } } }); ``` - Mutations中的函數(shù)必須都是同步函數(shù),異步函數(shù)都要寫在Actions中。
在組件中提交Mutation$store.commit('xxx','yyy')
當(dāng)然也有對應(yīng)的輔助函數(shù)幫助我們映射到對應(yīng)的methods方法中
import { mapMutations } from 'vuex';
export default {methods: {...mapMutations(['event1','event2']),...mapMutations({eventAlias: 'event3' //將this.eventAlias()映射為this.$store.commit('event3')})}
}; Action
Action與Mutation的不同在于Action不直接修改狀態(tài)只是做commit一個(gè)mutation、然后就是可以做異步操作。
簡單的Action
const INCREASE_COUNT = 'INCREASE_COUNT';
const store = new Vuex.Store({state: {count: 0},mutations: {[INCREASE_COUNT](state) {state.count++;}},actions: {add({ commit }) {commit(INCREASE_COUNT);}}
}); 看到這我們或許以為Actions的作用與Mutations不一樣么,對到現(xiàn)在為止作用是一樣的,但是Actions可以執(zhí)行異步操作
const INCREASE_COUNT = 'INCREASE_COUNT';
const store = new Vuex.Store({state: {count: 0},mutations: {[INCREASE_COUNT](state) {state.count++;}},actions: {add({ commit }) {setTimeout(() => {commit(INCREASE_COUNT);}, 1000);}}
}); Action的分發(fā)也有兩種方式
普通風(fēng)格分發(fā)方式
store.dispatch('actionName', {params: '參數(shù)'
}); 對象風(fēng)格分發(fā)方式
store.dispatch({type: 'actionName',params: '參數(shù)'
}); 輔助函數(shù)mapActions用法與mapMutations無異
代碼鏈接
methods: {...mapActions({add: 'add'})
} 組合Action
actions: {actionA ({ commit }) {return new Promise((resolve, reject) => {setTimeout(() => {commit('someMutation')resolve()}, 1000)})},// ...actionB ({ dispatch, commit }) {return dispatch('actionA').then(() => {commit('someOtherMutation')})}
}
store.dispatch('actionA').then(() => {// ...
})
或者
store.dispatch('actionB'); Module
如果項(xiàng)目龐大的話我們需要維護(hù)很多狀態(tài),這時(shí)候store會(huì)變得非常龐大,我們就需要store分割成很多模塊(Module),每個(gè)模塊同樣擁有自己的state,getters,mutations,actions以及modules
const moduleA = {state: {a: 'A'}
};
const moduleB = {state: {a: 'B'}
};
const store = new Vuex.Store({modules: {moduleA,moduleB}
});
console.log(store.state.moduleA.a); //A
console.log(store.state.moduleB.a); //B -
模塊內(nèi)部的mutation與getter接收的第一個(gè)參數(shù)都是局部狀態(tài)對象
``` 例如 const moduleA = { state: { price: 50, count: 2 }, getters: { totalPrice: state => state.price*state.count }, mutations: { decreaseCount(state) { state.count--; } } }; ``` -
模塊內(nèi)部的getter,根節(jié)點(diǎn)的狀態(tài)會(huì)在第三個(gè)參數(shù)展示出來。
``` const moduleB = { state: { count: 2 }, getters: { sum: (state, getters, rootState) => state.count+rootState.count } }; const store = new Vuex.Store({ state: { count: 1 }, modules: { moduleB } }); console.log(store.getters.sum);// 3 ```
注:根節(jié)點(diǎn)的狀態(tài)就是指的store.state -
模塊內(nèi)部的action,局部狀態(tài):
``` const moduleC = { state: { count: 2 }, mutations: { increaseCount(state) { state.count++; } }, actions: { addCount({ commit, state, rootState }) { let sum = state.count + rootState.count; if(sum % 3 === 0) { commit('increaseCount'); } } } }; const store = new Vuex.Store({ state: { count: 1 }, modules: { moduleC } }); console.log(store.state.moduleC.count);// 2 store.dispatch('addCount'); console.log(store.state.moduleC.count);// 3 ```context.state根部狀態(tài):context.rootState
命名空間
默認(rèn)情況下,模塊內(nèi)部的 action、mutation 和 getter 是注冊在全局命名空間的——這樣使得多個(gè)模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)。
如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過添加 namespaced: true 的方式使其成為命名空間模塊。當(dāng)模塊被注冊后,它的所有 getter、action 及 mutation 都會(huì)自動(dòng)根據(jù)模塊注冊的路徑調(diào)整命名。(摘自官網(wǎng))
注1:特別提出:模塊內(nèi)的state是嵌套的,namespaced屬性不會(huì)對其產(chǎn)生絲毫影響。
注2:沒加namespaced: true的模塊會(huì)繼承父模塊的命名空間
代碼鏈接
const store = new Vuex.Store({modules: {moduleA: {namespaced: true,getters: {count: () => 0 // store.getters['moduleA/count']},modules: {moduleB: {getters: {count1: () => 1 //繼承了父模塊的命名空間 store.getters['moduleA/count1']}},moduleC: {namespaced: true,getters: {count2: () => 2 //繼承了父模塊的命名空間 store.getters['moduleA/moduleC/count1']}}}}}
});
console.log(store.getters['moduleA/count']);// 0
console.log(store.getters['moduleA/count1']);// 1
console.log(store.getters['moduleA/moduleC/count2']);// 2 在命名空間模塊內(nèi)部訪問全局內(nèi)容
模塊內(nèi)部希望使用全局state與全局getter
-
rootState與rootGetters會(huì)作為getter第三個(gè)第四個(gè)參數(shù)傳入如
``` someGetter: (state, getters, rootState, rootGetters) => { //todo } ``` ``` const store = new Vuex.Store({ getters: { someOtherGetter: state => 'ROOT' }, modules: { moduleA: { namespaced: true, getters: { someGetter(state,getters,rootState,rootGetters) { return getters.someOtherGetter; // INSIDE // return rootGetters.someOtherGetter; // ROOT }, someOtherGetter: state => "INSIDE" } } } }); console.log(store.getters['moduleA/someGetter']); ``` -
也會(huì)通過
``` someAction:(context) => { //todo } 或者 someAction:({ commit, dispatch, rootState, rootGetters }) => { //todo } ```context.rootState與context.rootGetts傳入
如
模塊內(nèi)部希望使用全局mutation與全局action,只需要在執(zhí)行分發(fā)或者提交的時(shí)候在第三個(gè)參數(shù)位置傳入{ root: true }
如
dispatch('someOtherAction', null, {root: true});
commit('someOtherMutation', null, {root: true}); 栗子(沒有寫mutation與state的可以自行嘗試)
const store = new Vuex.Store({getters: {someOtherGetter: state => 'ROOT'},actions: {someOtherAction() {console.log('ROOT_ACTION');}},modules: {moduleA: {namespaced: true,getters: {someGetter(state,getters,rootState,rootGetters) {return getters.someOtherGetter; // INSIDE// return rootGetters.someOtherGetter; // ROOT},someOtherGetter: state => "INSIDE"},actions: {someAction({ dispatch, getters, rootGetters }) {console.log(getters.someOtherGetter);//INSIDEconsole.log(rootGetters.someOtherGetter);//ROOTdispatch('someOtherAction');//INSIDE_ACTIONdispatch('someOtherAction', null, {root: true});//ROOT_ACTION},someOtherAction() {console.log('INSIDE_ACTION');}}}}
});
console.log(store.getters['moduleA/someGetter']);
store.dispatch('moduleA/someAction'); 當(dāng)我們將store里的狀態(tài)映射到組件的時(shí)候,會(huì)出現(xiàn)下面的問題
computed: {...mapState({b1:state => state.moduleA.moduleB.b1,b2:state => state.moduleA.moduleB.b2})
},
methods: {...mapActions({'moduleA/moduleB/action1','moduleA/moduleB/action2'})
}
是不是比較繁瑣,代碼太過冗余。 當(dāng)然mapState、mapGetters、mapMutations、mapActions這些輔助函數(shù)都可以將空間名稱字符串作為第一個(gè)參數(shù)傳遞,這樣上面的例子可以簡化成
computed: {...mapState('moduleA/moduleB', {b1:state => state.b1,b2:state => state.b2})
},
methods: {...mapActions('moduleA/moduleB', {'action1','action2'})
} 當(dāng)然還有一個(gè)方法,使用Vuex提供的createNamespacedHelpers創(chuàng)建基于某個(gè)命名空間函數(shù),它返回一個(gè)對象,對象里有新的綁定在給定命名空間值上的組件綁定輔助函數(shù)。
import { createNamespacedHelpers } from 'vuex';
const { mapState , mapActions } = createNamespacedHelpers('moduleA/moduleB');
export default {computed: {...mapState({b1:state => state.b1,b2:state => state.b2})},methods: {...mapActions({'action1','action2'})}
} 模塊重用
有時(shí)我們可能需要?jiǎng)?chuàng)建一個(gè)模塊的多個(gè)實(shí)例:
- 創(chuàng)建多個(gè) store,他們公用同一個(gè)模塊
- 在一個(gè) store 中多次注冊同一個(gè)模塊
如果我們使用一個(gè)純對象來聲明模塊的狀態(tài),那么這個(gè)狀態(tài)對象會(huì)通過引用被共享,導(dǎo)致狀態(tài)對象被修改時(shí) store 或模塊間數(shù)據(jù)互相污染的問題。
實(shí)際上這和 Vue 組件內(nèi)的 data 是同樣的問題。因此解決辦法也是相同的——使用一個(gè)函數(shù)來聲明模塊狀態(tài)
const MyReusableModule = {state () {return {foo: 'bar'}},// mutation, action 和 getter 等等...
} 原文地址:https://segmentfault.com/a/1190000012645384
轉(zhuǎn)載于:https://www.cnblogs.com/lalalagq/p/9960288.html
總結(jié)
以上是生活随笔為你收集整理的Vuex-一个专为 Vue.js 应用程序开发的状态管理模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信两字网名大全
- 下一篇: 死精症影响生育怎么治