第十六節:Vuex4.x 簡介及state、getters、mutations、actions詳解(OptionApi 和 CompositionApi)


一. 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: {}
})
View Code

(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')
View Code

 

二. 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;
}
View Code

useState.js

import { useStore, mapState } from 'vuex'
import { useMapper } from './useMapper'

/* 
   封裝在setup中獲取state中的值
   ①:params: 需要獲取的參數名, 可以是數組,也可以是對象
   分別對應兩種調用方式
 */
export function useState(params) {
    return useMapper(params, mapState);
}
View Code

useGetters.js

import { useStore, mapGetters } from 'vuex'
import { useMapper } from './useMapper'

/* 
   封裝在setup中獲取getters中的值
   ①:params: 需要獲取的參數名, 可以是數組,也可以是對象
   分別對應兩種調用方式
 */
export function useGetters(params) {
    return useMapper(params, mapGetters);
}
View Code

  

四. 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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM