摘要:的出現就是為了解決這一狀況。基本上就是一個容器,它包含著你的應用中大部分的狀態。無法直接修改的狀態。如果有些狀態嚴格屬于單個組件,最好還是作為組件的局部狀態。因此解決辦法也是相同的使用一個函數來聲明模塊狀態和等等
為什么會出現Vuex
非父子關系的組件如何進行通信?(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++; }); } }
查看效果
但是當我們需要修改這個操作的時候,我們需要動3個地方,倘若項目小的話還倒好說,但是對于大項目組件間交互很多的概況,Event Bus就會表現的很吃力。Vuex的出現就是為了解決這一狀況。
Vuex介紹安裝:
script標簽引入
https://unpkg.com/vuex
NPM
npm install vuex --save-dev
import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex);
每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有以下兩點不同:
Vuex的狀態存儲是響應式的。當Vuex的狀態屬性發生變化時,相應的組件也會更新。
無法直接修改Vuex的狀態。只能通過顯式的提交(commit)。
簡單的Store
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increase(state) { state.count++; } } }); store.commit("increase"); console.log(store.state.count); // 1State
從上文我們知道Vuex是響應式的,我們如何在Vue實例中使用Vuex中的實例呢,自然離不開計算屬性computed了。
在 Vue 組件中獲得 Vuex 狀態
//倉庫 const store = new Vuex.Store({ state: { count: 1 } }); //foo組件 const foo = { template: `{{ count }}`, computed: { count: () => store.state.count } };這時候問題就來了,如果這樣進行引入store的話,組件就會以來全局狀態單例。
Vuex 通過 store 選項,提供了一種機制將狀態從根組件“注入”到每一個子組件中(需調用 Vue.use(Vuex))://掛載App new Vue({ el: "#app", //注入store選項 store, render: h => h(App) }); //foo組件 const foo = { template: `{{ count }}`, computed: { count() { // 不能使用箭頭函數,不然this就不是Vue實例了 // 通過this.$store獲取到store實例 return this.$store.state.count } } };如果有很多狀態需要映射,我們豈不是要寫好多代碼,這時候需要用到mapState輔助函數,使用 mapState 輔助函數幫助我們生成計算屬性。
輔助函數 實例1const foo = { template: `count: {{ count }} countAlias: {{ countAlias }} countPlusLocalCount: {{ countPlusLocalCount }}`, data() { return { localCount: 2 }; }, computed: Vuex.mapState({ //只處理倉庫中的count count: state => state.count*2, //映射 countAlias: "count", //需要用到Vue實例的話不能使用箭頭函數不然this無法獲取到Vue實例 countPlusLocalCount(state) { return state.count + this.localCount; } }) };當然當映射名與state中屬性名相同時,可以通過mapState(["count"])這種形式書寫。
因為組件本身也有許多計算屬性直接使用mapState的話無法擴充computed了,這時候我們可以使用ES新屬性: 對象展開運算符computed: { localComputed() { return this.count; }, ...Vuex.mapState({ //只處理倉庫中的count count: state => state.count*2, //映射 countAlias: "count", //需要用到Vue實例的話不能使用箭頭函數不然this無法獲取到Vue實例 countPlusLocalCount(state) { return state.count + this.localCount; } }) }ATT:使用 Vuex 并不意味著你需要將所有的狀態放入 Vuex。雖然將所有的狀態放到 Vuex 會使狀態變化更顯式和易調試,但也會使代碼變得冗長和不直觀。如果有些狀態嚴格屬于單個組件,最好還是作為組件的局部狀態。你應該根據你的應用開發需要進行權衡和確定。
Getter有時候我們需要過濾/處理state中的屬性值,就像Vue實例中我們需要處理data數據一樣,Vue有computed。Vuex同樣提供給我們一個屬性getter,getter就是Vuex的“計算屬性”。
Getter接收state作為第一個參數//倉庫 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 }]當然Getter同樣有輔助函數 mapGetters將 store 中的 getter 映射到局部計算屬性
直接上對象展開運算符了
輔助函數 實例1//foo組件 const foo = { template: `femaleList: {{ femaleList }}`, computed: { // 屬性名相同的不再闡述 ...Vuex.mapGetters({ femaleList: "getFemaleUsers" }) } }; Mutation更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation
提交時額外的參數被稱為載荷
普通風格提交方式store.commit("mutationName", { params: "參數" });對象風格提交方式
store.commit({ type: "mutationName", params: "參數" });注意點:
設置對象新屬性時
方法1:Vue.set(obj, "newVal", 100)
方法2:obj = {...obj, newVal: 100};
Mutations的事件類型盡量使用常量,并用一個文件進行維護。方便協同開發以及維護。
// 存儲事件名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中的函數必須都是同步函數,異步函數都要寫在Actions中。
在組件中提交Mutation
$store.commit("xxx","yyy")
當然也有對應的輔助函數幫助我們映射到對應的methods方法中import { mapMutations } from "vuex"; export default { methods: { ...mapMutations([ "event1", "event2" ]), ...mapMutations({ eventAlias: "event3" //將this.eventAlias()映射為this.$store.commit("event3") }) } };ActionAction與Mutation的不同在于Action不直接修改狀態只是做commit一個mutation、然后就是可以做異步操作。
簡單的Actionconst 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不一樣么,對到現在為止作用是一樣的,但是Actions可以執行異步操作
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的分發也有兩種方式
普通風格分發方式store.dispatch("actionName", { params: "參數" });對象風格分發方式
store.dispatch({ type: "actionName", params: "參數" });輔助函數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如果項目龐大的話我們需要維護很多狀態,這時候store會變得非常龐大,我們就需要store分割成很多模塊(Module),每個模塊同樣擁有自己的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
模塊內部的mutation與getter接收的第一個參數都是局部狀態對象
例如 const moduleA = { state: { price: 50, count: 2 }, getters: { totalPrice: state => state.price*state.count }, mutations: { decreaseCount(state) { state.count--; } } };
模塊內部的getter,根節點的狀態會在第三個參數展示出來。
注:根節點的狀態就是指的store.stateconst 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
模塊內部的action,局部狀態:context.state根部狀態:context.rootState
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命名空間
默認情況下,模塊內部的 action、mutation 和 getter 是注冊在全局命名空間的——這樣使得多個模塊能夠對同一 mutation 或 action 作出響應。
如果希望你的模塊具有更高的封裝度和復用性,你可以通過添加 namespaced: true 的方式使其成為命名空間模塊。當模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名。(摘自官網)
注1:特別提出:模塊內的state是嵌套的,namespaced屬性不會對其產生絲毫影響。
注2:沒加namespaced: true的模塊會繼承父模塊的命名空間
代碼鏈接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在命名空間模塊內部訪問全局內容
模塊內部希望使用全局state與全局getter
rootState與rootGetters會作為getter第三個第四個參數傳入
如
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"]);
也會通過context.rootState與context.rootGetts傳入
如someAction:(context) => { //todo } 或者 someAction:({ commit, dispatch, rootState, rootGetters }) => { //todo }模塊內部希望使用全局mutation與全局action,只需要在執行分發或者提交的時候在第三個參數位置傳入{ 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);//INSIDE console.log(rootGetters.someOtherGetter);//ROOT dispatch("someOtherAction");//INSIDE_ACTION dispatch("someOtherAction", null, {root: true});//ROOT_ACTION }, someOtherAction() { console.log("INSIDE_ACTION"); } } } } }); console.log(store.getters["moduleA/someGetter"]); store.dispatch("moduleA/someAction");當我們將store里的狀態映射到組件的時候,會出現下面的問題
computed: { ...mapState({ b1:state => state.moduleA.moduleB.b1, b2:state => state.moduleA.moduleB.b2 }) }, methods: { ...mapActions({ "moduleA/moduleB/action1", "moduleA/moduleB/action2" }) } 是不是比較繁瑣,代碼太過冗余。當然mapState、mapGetters、mapMutations、mapActions這些輔助函數都可以將空間名稱字符串作為第一個參數傳遞,這樣上面的例子可以簡化成
computed: { ...mapState("moduleA/moduleB", { b1:state => state.b1, b2:state => state.b2 }) }, methods: { ...mapActions("moduleA/moduleB", { "action1", "action2" }) }當然還有一個方法,使用Vuex提供的createNamespacedHelpers創建基于某個命名空間函數,它返回一個對象,對象里有新的綁定在給定命名空間值上的組件綁定輔助函數。
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" }) } }模塊重用
有時我們可能需要創建一個模塊的多個實例:創建多個 store,他們公用同一個模塊
在一個 store 中多次注冊同一個模塊
如果我們使用一個純對象來聲明模塊的狀態,那么這個狀態對象會通過引用被共享,導致狀態對象被修改時 store 或模塊間數據互相污染的問題。
實際上這和 Vue 組件內的 data 是同樣的問題。因此解決辦法也是相同的——使用一個函數來聲明模塊狀態
const MyReusableModule = { state () { return { foo: "bar" } }, // mutation, action 和 getter 等等... }文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90526.html
相關文章
Vuex10分鐘入門
摘要:它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。寫需要的組件創建一個組件,懟下面的內容,你喜歡彬哥哪一點創建一個展示組件我喜歡彬哥打開,刪掉沒用的東西,直接懟下面的代碼,到這里,架子就搭好了。 通過本文你將: 1.知道什么是Vuex. 2.知道為什么要用Vuex. 3.能跑一個Vuex的例子。 4.了解相關概念,面試的時候能說出一個所以然 5...
Vue.js狀態管理模式 Vuex
摘要:是一個專為應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。通過方法觸發在內部執行異步操作對象形式傳參以載荷形式分發 showImg(https://segmentfault.com/img/bVDxBu?w=701&h=551); vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理...
vue 項目實戰 (vue全家桶之--- vuex)
摘要:官方文檔對的解讀是是一個專為應用程序開發的狀態管理模式。而就是為了解決這樣的問題,把組件的共享狀態抽取出來,以一個全局單例模式管理。我們還可以傳入參數傳入一個對象可以修改中的狀態,但是并不是說隨隨便便修改的比如說老規矩先安裝 npm install vuex --save 在看下面內容之前 你應該大概的看了一邊vuex官方的文檔對vuex有個大概對了解 首先 vuex 是什么? vuex...
發表評論
0條評論
Freeman
男|高級講師
TA的文章
閱讀更多
seq2seq
閱讀 3407·2023-04-26 02:41
pypi
閱讀 2454·2023-04-26 00:14
NameSilo域名優惠碼集合匯總(無套路且可支付寶付款)
閱讀 2857·2021-08-11 10:22
百萬創作人的視頻創意靈感,也離不開“云”
閱讀 1284·2019-12-27 11:38
記一次用vue做的活動頁
閱讀 3575·2019-08-29 18:34
前端實例練習 - 圖片覆蓋層
閱讀 2384·2019-08-29 12:13
使用babel7構建完美的純ts開發環境
閱讀 2955·2019-08-26 18:26
UCloud云主機自定義鏡像
閱讀 1853·2019-08-26 16:49
閱讀需要支付1元查看