住房城鄉(xiāng)建設(shè)管理委員官方網(wǎng)站小程序開(kāi)發(fā)公司哪里強(qiáng)
文章目錄
- 全局狀態(tài)管理模式Vuex
- vuex是什么?
- 什么是“狀態(tài)管理模式”?
- vuex的應(yīng)用場(chǎng)景
- Vuex安裝
- 開(kāi)始
- 核心概念
- 一、State
- 1、單一狀態(tài)樹(shù)
- 2、在 Vue 組件中獲得 Vuex 狀態(tài)
- 3、mapState輔助函數(shù)
- 二、Getter
- 三、Mutation
- 1、提交載荷(Payload)
- 2、對(duì)象風(fēng)格的提交方式
- 3、使用常量代替Mutation事件類型
- 4、Mutation必須是同步函數(shù)
- 5、在組件中提交Mutation
- 四、Action
全局狀態(tài)管理模式Vuex
💡 Tips:由于需要使用到全局變量,vue3做全局狀態(tài)管理和它搭配最好的是Pinia,但是菠蘿是和組合式API搭配一起使用更好一些。和Pinia功能一模一樣的是Vuex插件,這個(gè)插件公司相對(duì)使用得較多。
- vue3對(duì)應(yīng)的是vuex4版本,vue2對(duì)應(yīng)的是vuex3版本
- vue3的官網(wǎng)是找不到vuex,在vue2的官網(wǎng)https://vuex.vuejs.org/zh/
vuex是什么?
Vuex是專門為Vue設(shè)計(jì)的狀態(tài)管理模式+庫(kù)。我們通常稱之為全局狀態(tài)管理模式,它能管理所有組件的狀態(tài)。(狀態(tài)在vue里面可以理解為響應(yīng)式數(shù)據(jù))
什么是“狀態(tài)管理模式”?
狀態(tài)管理自管理應(yīng)用包含三部分:
- 狀態(tài),驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源
- 視圖,以聲明方式將狀態(tài)映射到視圖
- 操作,響應(yīng)式的在視圖上的用戶輸入導(dǎo)致的狀態(tài)變化
當(dāng)我們的應(yīng)用遇到多個(gè)組件共享狀態(tài)(數(shù)據(jù))時(shí),單項(xiàng)數(shù)據(jù)流的簡(jiǎn)潔性很容易被破壞。意思是說(shuō)vue是單項(xiàng)數(shù)據(jù)流,數(shù)據(jù)只能從父的去改變子的,子的是不能操作父的。
vuex的應(yīng)用場(chǎng)景
- 多個(gè)視圖(組件)依賴于同一個(gè)狀態(tài)(數(shù)據(jù))
- 來(lái)自不同視圖(組件)的行為需要變更同一狀態(tài)(數(shù)據(jù)),指的是多個(gè)組件需要更改同一個(gè)數(shù)據(jù)
問(wèn)題一:傳參的方法對(duì)于多層嵌套的組件將會(huì)非常繁瑣,并且對(duì)于兄弟組件間的狀態(tài)傳遞無(wú)能為力。
問(wèn)題二:我們經(jīng)常會(huì)采用父子組件直接引用或者通過(guò)事件來(lái)變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會(huì)導(dǎo)致無(wú)法維護(hù)的代碼。
因此,我們把組件的共享狀態(tài)抽取出來(lái),以一個(gè)全局單例模式管理
Vuex安裝
- 項(xiàng)目安裝命令npm init vue@latest
- 依賴安裝npm i
- Vuex安裝npm install vuex@next --save
開(kāi)始
可以創(chuàng)建一個(gè)store文件夾存放index.js文件
// 從vuex里面解構(gòu)createStore方法
import { createStore } from "vuex";
// 創(chuàng)建倉(cāng)庫(kù)實(shí)例
const store = createStore()
// 暴露實(shí)例
export default store;
從入口文件main.js引入倉(cāng)庫(kù)實(shí)例
這里注意:如果路徑默認(rèn)以文件夾結(jié)尾,例如這里的./store相當(dāng)于./store/index.js
import { createApp } from "vue";
import App from "./App.vue";// 引入倉(cāng)庫(kù)實(shí)例
import store from "./store";createApp(App).use(store).mount("#app");
倉(cāng)庫(kù)的state就是全局的數(shù)據(jù),相當(dāng)于組件的data,語(yǔ)法是data一樣是函數(shù)返回對(duì)象
// 要?jiǎng)?chuàng)建倉(cāng)庫(kù)實(shí)例
import { createStore } from "vuex";const store = createStore({// 倉(cāng)庫(kù)的state就是全局的數(shù)據(jù),相當(dāng)于組件的datastate() {return {count: 10,};},
});export default store;
在跟組件里面引入兩個(gè)子組件
<template><h2>vuex-demo</h2><Child1 /><hr /><Child2 />
</template><script>
import Child1 from "./components/Child1.vue";
import Child2 from "./components/Child2.vue";export default {components: {Child1,Child2,},
};
</script>
通過(guò)this.store在子組件中拿到倉(cāng)庫(kù)的數(shù)據(jù),這里的this.store在子組件中拿到倉(cāng)庫(kù)的數(shù)據(jù),這里的this.store在子組件中拿到倉(cāng)庫(kù)的數(shù)據(jù),這里的this.store是倉(cāng)庫(kù)實(shí)例是個(gè)對(duì)象類似于this.$route
- 可以通過(guò)this.$store.state.xxx來(lái)獲取倉(cāng)庫(kù)數(shù)據(jù)
- 計(jì)算屬性會(huì)根據(jù)已有的值去計(jì)算一個(gè)新的結(jié)果,并且已有的值可以作為計(jì)算屬性的依賴
export default {mounted() {return this.$store.state.count;},computed: {count() {return this.$store.state.count;},},
}
- 倉(cāng)庫(kù)數(shù)據(jù)不能使用data來(lái)接收,會(huì)導(dǎo)致響應(yīng)式失效
// 倉(cāng)庫(kù)數(shù)據(jù)不能使用data來(lái)接受,會(huì)導(dǎo)致響應(yīng)式失效data() {return {count: this.$store.state.count,};},
- 直接修改是不符合規(guī)范的
methods: {fn() {// 直接修改是不符合規(guī)范this.$store.state.count++;},},
- mutations是唯一可以改變state的地方,里面放函數(shù),子組件可以通過(guò)commit方法去調(diào)用這個(gè)函數(shù)改變倉(cāng)庫(kù)數(shù)據(jù)
mutations: {increment(state) {state.count++;},}
methods: {fn() {this.$store.commit("increment");},}
核心概念
一、State
1、單一狀態(tài)樹(shù)
- 一個(gè)項(xiàng)目里面只有一個(gè)倉(cāng)庫(kù),只有一個(gè)state。
- 單一狀態(tài)樹(shù)和模塊化并不沖突,項(xiàng)目會(huì)分模塊,意味著會(huì)有多個(gè)state
2、在 Vue 組件中獲得 Vuex 狀態(tài)
- 由于 Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的,從 store 實(shí)例中讀取狀態(tài)最簡(jiǎn)單的方法就是在計(jì)算屬性中返回某個(gè)狀態(tài)
3、mapState輔助函數(shù)
💡Tips:當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)的時(shí)候,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余??梢允褂?mapState 輔助函數(shù)幫助我們生成計(jì)算屬性,較少代碼量
- mapState數(shù)組寫(xiě)法
- 當(dāng)映射的計(jì)算屬性的名稱與 state 的子節(jié)點(diǎn)名稱相同時(shí),我們也可以給 mapState 傳一個(gè)字符串?dāng)?shù)組。
// 重復(fù)和冗余
// computed: {// count() {// return this.$store.state.count;// },// name() {// return this.$store.state.name;// },// sex() {// return this.$store.state.sex;// },// likes() {// return this.$store.state.likes;// },// },// mapState的數(shù)組寫(xiě)法computed: mapState(["count", "name", "sex", "likes"]),
- mapState對(duì)象寫(xiě)法
- 可以重命名
- 可以寫(xiě)函數(shù),改造數(shù)據(jù)
computed: mapState({// 重命名num: "count",// 函數(shù)寫(xiě)法用于做數(shù)據(jù)的改造name: (state) => "親愛(ài)的" + state.name,sex: (state) => {return state.sex === 1 ? "男" : "女";},// 如果需要用到this,要把箭頭函數(shù)轉(zhuǎn)成普通函數(shù)likes(state) {return state.likes.concat(this.like);},}),
由于組件自身就可以寫(xiě)computed屬性,但是組件里面的選項(xiàng)是不能重復(fù)的,如果我們寫(xiě)兩個(gè)computed那么后面的對(duì)象肯定會(huì)覆蓋前面的。
解決方法:使用擴(kuò)展運(yùn)算符或者Object.assign() 淺拷貝合并對(duì)象
computed: {...mapState({// 重命名num: "count",// 函數(shù)寫(xiě)法用于做數(shù)據(jù)的改造name: (state) => "親愛(ài)的" + state.name,sex: (state) => {return state.sex === 1 ? "男" : "女";},// 如果需要用到this,要把箭頭函數(shù)轉(zhuǎn)成普通函數(shù)likes(state) {return state.likes.concat(this.like);},}),doubleNum() {return this.num2 * 2;},},
二、Getter
場(chǎng)景:如果我們的各自組件都需要封裝一個(gè)相同的函數(shù),例如計(jì)算屬性里面封裝一個(gè)時(shí)間戳函數(shù),我們需要在其他組件中復(fù)用這個(gè)函數(shù),這樣重復(fù)的代碼就很多,這是我們希望將函數(shù)寫(xiě)在倉(cāng)庫(kù)里給其他組件使用。
- getters相當(dāng)于倉(cāng)庫(kù)的計(jì)算屬性
- state這個(gè)參數(shù)就是為了拿到state(){}里面的數(shù)據(jù)
- getters這個(gè)參數(shù)表示一個(gè)getters不僅可以依賴倉(cāng)庫(kù)的數(shù)據(jù),還可以依賴另一個(gè)getters。(一般用不上)
- getters傳參
- mapGetters 輔助函數(shù),mapGetters僅僅是將 store 中的 getter 映射到局部計(jì)算屬性
// getters相當(dāng)于是倉(cāng)庫(kù)的計(jì)算屬性getters: {timeStr(state, getters) {const date = new Date(state.time);const Y = date.getFullYear() + "-";const M =(date.getMonth() + 1 < 10? "0" + (date.getMonth() + 1): date.getMonth() + 1) + "-";const D =(date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " ";const h =(date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":";const m =(date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) +":";const s =date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();return Y + M + D + h + m + s;},timeStr2(state, getters) {return getters.timeStr + "!!!";},count2: (state) => (n) => state.count * n,
});
三、Mutation
mutation是唯一可以改變state的方法。mutation里面是放函數(shù),通過(guò)this.store.commit方法去觸發(fā)mutation里面的函數(shù)。
1、提交載荷(Payload)
- 可以接受第二個(gè)參數(shù)payload(載荷),表示參數(shù)
- 一般情況下payload建議寫(xiě)成對(duì)象的形式
- 對(duì)象可以傳多個(gè)值
- 方便維護(hù)
syncAgeAdd(state, payload) {state.age += payload.n;},
2、對(duì)象風(fēng)格的提交方式
add(n) {// 如何去觸發(fā)倉(cāng)庫(kù)的mutations的函數(shù)this.$store.commit("increment2", { num: n });// 等價(jià)于下面的對(duì)象風(fēng)格的提交方式this.$store.commit({// type: "increment2",type: INCREMENT2,num: n,});},
3、使用常量代替Mutation事件類型
使用常量將字符串存起來(lái)使用
export const INCREMENT2 = "increment2";
4、Mutation必須是同步函數(shù)
- 每一次調(diào)用mutation的函數(shù)的時(shí)候,在開(kāi)發(fā)工具里面都會(huì)產(chǎn)生一條記錄(就是快照)
- 產(chǎn)生快照的時(shí)間是mutation函數(shù)調(diào)用的時(shí)間,而不是數(shù)據(jù)改變的時(shí)間。
- 會(huì)讓開(kāi)發(fā)工具里面快照的值有錯(cuò)誤(例如下面使用定時(shí)器異步代碼)
addage(state) {setTimeout(() => {state.age++;}, 2000);
},
5、在組件中提交Mutation
- mapMutations輔助函數(shù)是使用在methods里面
- 下面代碼將倉(cāng)庫(kù)的addage函數(shù)映射到了組件的methods里面,變成了methods里面有addage函數(shù)
methods: {
ageadd() {// this.$store.commit("addage");this.addage();},// 將倉(cāng)庫(kù)的addage函數(shù)映射到了組件的methods里面,// 變成了methods里面有addage函數(shù)...mapMutations(["addage", INCREMENT2]),}
四、Action
action類似于mutation,不同的在于:
- Action 提交的是 mutation,而不是直接變更狀態(tài)。
- Action 可以包含任意異步操作。
- 通過(guò)dispatch的方法來(lái)調(diào)用倉(cāng)庫(kù)的actions的函數(shù)
- actions是不能直接修改state的,只能通過(guò)調(diào)用mutations的函數(shù)
<template><h3>actions</h3><div>age: {{ age }} - <button @click="fn">async age++</button></div>
</template><script>
import { mapState, mapActions } from "vuex";export default {computed: mapState(["age"]),methods: {fn() {// 通過(guò)dispatch的方法來(lái)調(diào)用倉(cāng)庫(kù)的actions的函數(shù)// this.$store.dispatch("asyncAgeAdd", { n: 4 });this.asyncAgeAdd({ n: 4 });},...mapActions(["asyncAgeAdd"]),},
};
</script>