一. Vuex簡介
1. 簡介
(官網地址:https://next.vuex.vuejs.org/zh/index.html 在Vue2中使用的詳見:https://www.cnblogs.com/yaopengfei/p/14571316.html 本節采用的版本號【4.0.2】)
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式 + 庫。它采用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
如果您不打算開發大型單頁應用,使用 Vuex 可能是繁瑣冗余的。確實是如此——如果您的應用夠簡單,您最好不要使用 Vuex。一個簡單的store模式(或者父子組件傳值、緩存等)就足夠您所需了。但是,如果您需要構建一個中大型單頁應用,您很可能會考慮如何更好地在組件外部管理狀態,Vuex 將會成為自然而然的選擇。

2. 核心總結
在Vuex中,有五大核心模塊,分別是state、getters、mutations、actions、module,下圖是每個模塊的作用,以及在OptionApi 和 Composition Api中對應的 $store 、輔助函數的兩種寫法。

3. 快速入門
(1). 在store文件夾中創建index.js文件
import { createStore } from 'vuex'
// 導入相關常量
import { ADD_N } from './mutation-type'
export default createStore({
state() {
return {
counter1: 100,
name: 'ypf',
age: 18,
height: '1.82m'
}
},
getters: {
// 第一個參數是固定參數state, 用來獲取上面state中的對象,
ageInfo(state) {
return `我的年齡是${state.age}`;
},
//可以返回一個函數,來實現傳參
// 外界傳過來的值這里用msg接收,這里的getters主要是用來獲取其它getter屬性的
nameInfo(state, getters) {
return function(msg) {
return `我的名字是${state.name},${msg},${getters.ageInfo}`;
}
},
},
mutations: {
increment(state) {
state.counter1++;
},
decrement(state) {
state.counter1--
},
/*
參數說明:
state:用來獲取上面state中的數據
payLoad:用於接收外界傳遞過來的數據
*/
// payLoad可以是int類型,表示遞增數
incrementN(state, payLoad) {
state.counter1 += payLoad;
},
// payLoad可以是對象,比如 {myStep:20,name:'ypf'}
decrementN(state, payLoad) {
state.counter1 -= payLoad.myStep;
},
// payLoad可以是int類型,表示遞增數
[ADD_N](state, payLoad) {
state.counter1 += payLoad;
}
},
actions: {
/*
兩個參數:
(1). context:是一個和$store實例均有相同方法和屬性的對象,可以從其中獲取到commit方法來提交一個mutation,
或者通過 context.state 和 context.getters 來獲取 state 和 getters
可以解構為:{ commit, dispatch, state, rootState, getters, rootGetters }
(2). payLoad:外界傳遞過來的數據,可以是各種類型
*/
incrementAction(context, payLoad) {
setTimeout(() => {
context.commit('increment');
}, 1000);
},
// payLoad可以是一個對象 {myStep:20,myStep2:30}
decrementNAction({ commit, dispatch, state, rootState, getters, rootGetters }, payLoad) {
if (payLoad.myStep) {
commit('decrementN', payLoad);
} else {
commit('decrement');
}
},
// 通過promise實現捕獲異常的結束
testAdd(context) {
return new Promise((resolve, reject) => {
try {
context.commit('increment');
resolve('執行結束啦')
} catch (e) {
reject('出錯了')
}
});
}
},
modules: {}
})
(2). 在入口文件main.js中進行引入
import { createApp } from 'vue'
// 需要測試不同頁面
import App from './pages/09_action_optionApi/App.vue'
// 引入Vuex插件
import store from './store'
createApp(App).use(store).mount('#app')
二. state詳解
1. 作用
State 提供唯一的公共數據源,所有共享的數據都要統一放到 State 中進行存儲。
export default createStore({ state() { return { counter1: 100, name: 'ypf', age: 18, height: '1.82m' } }, })
2. Options Api中用法
寫法1: $store對象
<h4>counter1:{{$store.state.counter1}}</h4> <h4>name:{{$store.state.name}}</h4>
寫法2:輔助函數mapState
參數有兩種:①數組類型,原名映射 ②對象類型,自定義名
<template> <div> <!-- 2. mapState函數的用法 --> <p>2. mapState函數的用法</p> <h4>counter1:{{counter1}}</h4> <h4>name:{{name}}</h4> <h4>myCounter1:{{myCounter1}}</h4> <h4>myName:{{myName}}</h4> </div> </template> <script> import { mapState } from 'vuex' export default { computed: { // 1. 寫法1-原名映射(數組類型) ...mapState(['counter1', 'name']), // 2. 寫法2-自定義名(對象類型) ...mapState({ myCounter1: state => state.counter1, myName: state => state.name }) } } </script>
3. Composition Api中用法
寫法1: 通過useStore創建store對象
<template> <div> <!-- 1. 通過useStore拿到store后去獲取某個狀態即可 --> <h4>myCounter1:{{myCounter1}}</h4> <h4>myName:{{myName}}</h4> </div> </template> <script> import { mapState, useStore } from 'vuex'; import { computed } from 'vue'; export default { setup() { // 方案1-通過useStore拿到store后去獲取某個狀態即可 const store = useStore(); const myCounter1 = computed(() => store.state.counter1); const myName = computed(() => store.state.name); return { myCounter1, myName } } } </script>
寫法2: 自己封裝useState方法 【推薦!】
useState.js代碼
import { useStore, mapState } from 'vuex'
import {computed} from 'vue'
/*
封裝在setup中獲取state中的值
①:params: 需要獲取的參數名, 可以是數組,也可以是對象
分別對應兩種調用方式
*/
export function useState(params) {
const store = useStore();
const storeStateFns = mapState(params);
const storeState={};
Object.keys(storeStateFns).forEach(fnKey=>{
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey]=computed(fn);
})
return storeState;
}
使用代碼
<template> <div> <!-- 調用封裝后代碼 --> <h4>counter1:{{counter1}}</h4> <h4>name:{{name}}</h4> <h4>myCounter1:{{myCounter1}}</h4> <h4>myName:{{myName}}</h4> </div> </template> <script> // 導入封裝 import { useState } from '../../hooks/version1/useState' export default { setup() { // 寫法1:傳遞數組 const result1 = useState(['counter1', 'name']); // 寫法2:傳遞對象 const result2 = useState({ myCounter1: state => state.counter1, myName: state => state.name }); return { ...result1, ...result2 } } } </script>
三. getters詳解
1. 作用
getters用於對 State 中的數據進行加工處理形成新的數據。
① getters 可以對 State 中已有的數據加工處理之后形成新的數據,類似 Vue 的Computed。
② State 中數據發生變化,getters 的數據也會跟着變化。
注意:getters中聲明的是方法;
方法的第一個參數為state,可以獲取State中屬性。可以返回一個函數,來實現傳參。
export default createStore({ state() { return { counter1: 100, name: 'ypf', age: 18, height: '1.82m' } }, getters: { // 第一個參數是固定參數state, 用來獲取上面state中的對象, ageInfo(state) { return `我的年齡是${state.age}`; }, //可以返回一個函數,來實現傳參 // 外界傳過來的值這里用msg接收,這里的getters主要是用來獲取其它getter屬性的 nameInfo(state, getters) { return function(msg) { return `我的名字是${state.name},${msg},${getters.ageInfo}`; } }, }, })
2. Options Api中用法
寫法1: $store對象
<h4>{{$store.getters.ageInfo}}</h4> <h4>{{$store.getters.nameInfo('哈哈哈') }}</h4>
寫法2:輔助函數mapState
參數有兩種:①數組類型,原名映射 ②對象類型,自定義名
<template> <div> <!-- 2.輔助函數調用 --> <h4>{{ageInfo}}</h4> <h4>{{nameInfo('呵呵呵')}}</h4> <h4>{{myAgeInfo}}</h4> <h4>{{myNameInfo('嘿嘿嘿')}}</h4> </div> </template> <script> import { mapGetters } from 'vuex'; export default { computed: { // 輔助函數調用 // 寫法1-傳遞數組(原名映射) ...mapGetters(['ageInfo', 'nameInfo']), // 寫法2-傳遞對象(可以自定義名稱) ...mapGetters({ myAgeInfo: 'ageInfo', myNameInfo: 'nameInfo' }) } } </script>
3. Composition Api中用法
寫法1: 通過useStore拿到store后去獲取某個getters即可(不推薦,繁瑣)
<template> <div> <!-- 1.通過useStore拿到store后去獲取某個getters即可--> <h4>{{ageInfo2}}</h4> <h4>{{nameInfo2('嘿嘿嘿嘿1')}}</h4> </div> </template> <script> // 原始寫法的引入 import { computed } from 'vue'; import { useStore } from 'vuex'; export default { setup() { // 1. 通過useStore拿到store后去獲取某個getters即可(不推薦) var store = useStore(); const ageInfo2 = computed(() => store.getters.ageInfo); const nameInfo2 =computed(()=>store.getters.nameInfo);
return { ageInfo2, nameInfo2, } } } </script>
寫法2: 自己封裝useGetters方法
useGetters.js封裝
import { useStore, mapGetters } from 'vuex'
import {computed} from 'vue'
/*
封裝在setup中獲取getters中的值
①:params: 需要獲取的參數名, 可以是數組,也可以是對象
分別對應兩種調用方式
*/
export function useGetters(params) {
const store = useStore();
const storeStateFns = mapGetters(params);
const storeState={};
Object.keys(storeStateFns).forEach(fnKey=>{
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey]=computed(fn);
})
return storeState;
}
調用
<template> <div> <!-- 2.調用自己的封裝 --> <h4>{{ageInfo}}</h4> <h4>{{nameInfo('哈哈哈哈1')}}</h4> <h4>{{myAgeInfo}}</h4> <h4>{{myNameInfo('哈哈哈哈2')}}</h4> </div> </template> <script> // 自己封裝的引入 import { useGetters } from '../../hooks/version1/useGetters.js'; export default { setup() {// 2. 自己封裝(推薦) // 調用1-傳遞數組類型 var result1 = useGetters(['ageInfo', 'nameInfo']); // 調用2-傳遞對象類型 var result2 = useGetters({ myAgeInfo: 'ageInfo', myNameInfo: 'nameInfo' }); return { ageInfo2, nameInfo2, ...result1, ...result2 } } } </script>
補充state和getters抽離共同點代碼后,統一封裝:
useMapper.js
import { useStore } from 'vuex'
import {computed} from 'vue'
/*
抽離useState和useGetters中的通用邏輯
①:params: 需要獲取的參數名, 可以是數組,也可以是對象
分別對應兩種調用方式
②:fn:可以是mapGetters 或 mapState
*/
export function useMapper(params,fn) {
const store = useStore();
const storeStateFns = fn(params);
const storeState={};
Object.keys(storeStateFns).forEach(fnKey=>{
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey]=computed(fn);
})
return storeState;
}
useState.js
import { useStore, mapState } from 'vuex'
import { useMapper } from './useMapper'
/*
封裝在setup中獲取state中的值
①:params: 需要獲取的參數名, 可以是數組,也可以是對象
分別對應兩種調用方式
*/
export function useState(params) {
return useMapper(params, mapState);
}
useGetters.js
import { useStore, mapGetters } from 'vuex'
import { useMapper } from './useMapper'
/*
封裝在setup中獲取getters中的值
①:params: 需要獲取的參數名, 可以是數組,也可以是對象
分別對應兩種調用方式
*/
export function useGetters(params) {
return useMapper(params, mapGetters);
}
四. mutations詳解
1. 作用
mutations 用於變更State中 的數據。
① 只能通過 mutation 變更 State中 數據(actions也需要context.commit來間接的修改state),不可以直接操作 Store 中的數據。(禁止 通過 this.$store.state.count 獲取后直接修改)
② 通過這種方式雖然操作起來稍微繁瑣一些,但是可以集中監控所有數據的變化。
注:mutations中聲明方法,方法的第一個參數為state,可以獲取State中屬性,第二個參數可以自定義類型,進行傳遞。
mutation-type.js
// 自定義常量 export const ADD_N = 'add_n';
import { createStore } from 'vuex'
// 導入相關常量
import { ADD_N } from './mutation-type'
export default createStore({
state() {
return {
counter1: 100,
}
},
mutations: {
increment(state) {
state.counter1++;
},
decrement(state) {
state.counter1--
},
/*
參數說明:
state:用來獲取上面state中的數據
payLoad:用於接收外界傳遞過來的數據
*/
// payLoad可以是int類型,表示遞增數
incrementN(state, payLoad) {
state.counter1 += payLoad;
},
// payLoad可以是對象,比如 {myStep:20,name:'ypf'}
decrementN(state, payLoad) {
state.counter1 -= payLoad.myStep;
},
// payLoad可以是int類型,表示遞增數
[ADD_N](state, payLoad) {
state.counter1 += payLoad;
}
},
})
2. Options Api中用法
寫法1: $store對象
<h4><button @click="$store.commit('increment')">加1</button></h4> <h4><button @click="$store.commit('incrementN',20)">加N</button></h4>
寫法2:輔助函數mapMutations
參數有兩種:①數組類型,原名映射 ②對象類型,自定義名
<template> <div> <h4>counter:{{$store.state.counter1}}</h4> <p>2.mapMutations用法</p> <h4><button @click="decrement">減1</button></h4> <h4><button @click="decrementN({myStep:100})">減N</button></h4> <!-- 自定義名稱 --> <h4><button @click="myIncrement">加1</button></h4> <h4><button @click="myIncrementN(200)">加N</button></h4> </div> </template> <script> import { mapMutations } from 'vuex'; export default { methods: { // 輔助函數 // 寫法1-傳遞數組 ...mapMutations(['decrement', 'decrementN']), // 寫法2-傳遞對象 ...mapMutations({ myIncrement: 'increment', myIncrementN: 'incrementN' }), } </script>
補充:可以在自定義的方法你進行$store對象的調用、$store對象的特有對象調用、調用mapMutations中的方法
export default { methods: {// 當然也可以自己封裝個方法 test1() { // this.$store.commit('increment'); // 或 // this.myIncrementN(2); // 補充一個參數為對象類型的時候的特有寫法 // type屬性中寫方法名,其它屬性則為傳遞的對象屬性哦 // this.$store.commit({ // type: 'decrementN', // myStep: 50, // name:'ypf' // }) // 補充使用自定義常量 this.$store.commit(ADD_N, 1000); } }, }
3. Composition Api中用法
寫法1:
通過useStore創建store對象,這里的store對象就是OptionApi中的$store,然后使用commit方法進行調用。
寫法2.
直接調用mapMutations輔助函數,然后進行返回即可。
<template> <div> <h4>counter:{{$store.state.counter1}}</h4> <h4><button @click="decrement">減1</button></h4> <h4><button @click="decrementN({myStep:100})">減N</button></h4> <h4><button @click="myIncrement">加1</button></h4> <h4><button @click="myIncrementN(200)">加N</button></h4> <h4><button @click="add_n(200)">加N</button></h4> </div> </template> <script> import { mapMutations } from 'vuex'; // 導入自定義常量 import { ADD_N } from '../../store/mutation-type.js' export default { setup() { // setup中調用輔助函數mapMutations容易,直接調用即可,不用再自己封裝了 const result1 = mapMutations(['decrement', 'decrementN']); const result2 = mapMutations({ myIncrement: 'increment', myIncrementN: 'incrementN' }); // 使用自定義常量的方式 const result3 = mapMutations([ADD_N]); return { ...result1, ...result2, ...result3 } } } </script>
五. actions詳解
1. 作用
如果通過異步操作變更數據,必須通過 actions,而不能使用 mutations,但是在 action 中還是要通過觸發Mutation 的方式間接變更數據。
注:actions中的方法有兩個參數
(1). context:是一個和$store實例均有相同方法和屬性的對象,可以從其中獲取到commit方法來提交一個mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters, 可以解構為:{ commit, dispatch, state, rootState, getters, rootGetters }
(2). payLoad:外界傳遞過來的數據,可以是各種類型
import { createStore } from 'vuex'
export default createStore({
actions: {
/*
兩個參數:
(1). context:是一個和$store實例均有相同方法和屬性的對象,可以從其中獲取到commit方法來提交一個mutation,
或者通過 context.state 和 context.getters 來獲取 state 和 getters
可以解構為:{ commit, dispatch, state, rootState, getters, rootGetters }
(2). payLoad:外界傳遞過來的數據,可以是各種類型
*/
incrementAction(context, payLoad) {
setTimeout(() => {
context.commit('increment');
}, 1000);
},
// payLoad可以是一個對象 {myStep:20,myStep2:30}
decrementNAction({ commit, dispatch, state, rootState, getters, rootGetters }, payLoad) {
if (payLoad.myStep) {
commit('decrementN', payLoad);
} else {
commit('decrement');
}
},
// 通過promise實現捕獲異常的結束
testAdd(context) {
return new Promise((resolve, reject) => {
try {
context.commit('increment');
resolve('執行結束啦')
} catch (e) {
reject('出錯了')
}
});
}
},
})
2. Options Api中用法
寫法1. $store對象
<h4><button @click="$store.dispatch('incrementAction')">加1(延遲1s)</button></h4> <h4><button @click="$store.dispatch('decrementNAction',{myStep:100})">減N</button></h4>
寫法2. 輔助函數mapActions
<template> <div> <h4>counter:{{$store.state.counter1}}</h4> <p>2.mapActions用法</p> <h4><button @click="incrementAction">加1(延遲1s)</button></h4> <h4><button @click="decrementNAction({myStep:100})">減N</button></h4> <!-- 自定義名稱 --> <h4><button @click="myincrementAction">加1(延遲1s)</button></h4> <h4><button @click="mydecrementNAction({myStep:100})">減N</button></h4> <p>3.在自己封裝的方法中調用</p> <button @click="test1">test1</button> </div> </template> <script> import { mapActions } from 'vuex'; export default { methods: { // 輔助函數用法 // 1. 用法1-傳遞數組 ...mapActions(["incrementAction", "decrementNAction"]), // 2. 用法2-傳對象(自定義名稱) ...mapActions({ myincrementAction: "incrementAction", mydecrementNAction: "decrementNAction" }), test1() { // 1.$store也可以在自己定義的方法中使用哦 // this.$store.dispatch('incrementAction'); //2. 補充$store對象的特殊調用方式 this.$store.dispatch({ type: 'decrementNAction' }); // 或 // this.$store.dispatch({ // type: 'decrementNAction', // myStep:101 // }); } }, data() { return {}; } } </script>
補充:可以在自定義的方法你進行$store對象的調用、$store對象的特有對象調用、調用mapActions中的方法
3. Composition Api中用法
寫法1:
通過useStore創建store對象,然后調用dispatch方法
<template> <div> <h3>{{$store.state.counter1}}</h3> <p>1. 通過useStore拿到store后,然后調用dispatch進行分發</p> <h4><button @click="tADD">加1(延遲1s)</button></h4> </div> </template> <script> import { useStore } from 'vuex'; export default { setup() { // 方案1 通過useStore拿到store后,然后調用dispatch進行分發 const store = useStore(); const tADD = () => { store.dispatch('incrementAction'); }; return { tADD, } } } </script> <style scoped> </style>
寫法2:
調用輔助函數mapActions,直接返回即可
<template> <div> <h3>{{$store.state.counter1}}</h3> <p>2.mapActions用法</p> <h4><button @click="incrementAction">加1(延遲1s)</button></h4> <h4><button @click="decrementNAction({myStep:100})">減N</button></h4> <!-- 自定義名稱 --> <h4><button @click="myincrementAction">加1(延遲1s)</button></h4> <h4><button @click="mydecrementNAction({myStep:100})">減N</button></h4> <p>3. 測試捕獲異步的結束</p> <h4><button @click="tTest">測試捕獲異常結束</button></h4> </div> </template> <script> import { mapActions, useStore } from 'vuex'; export default { setup() {//方案二:使用輔助函數 (推薦!! 簡潔) // 在setup中直接使用輔助函數即可,不需自己再封裝了 const actions1 = mapActions(["incrementAction", "decrementNAction"]); // (自定義名稱) const actions2 = mapActions({ myincrementAction: "incrementAction", mydecrementNAction: "decrementNAction" }); // 測試捕獲異步的結束 const tTest=()=>{ store.dispatch('testAdd').then(res=>{ console.log(`獲取異步結束后的返回信息:${res}`); }); }; return { ...actions1, ...actions2, tTest } } } </script>
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
