zoukankan      html  css  js  c++  java
  • 第十六节: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.commmit来间接的修改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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    Unity的动态加载简单使用
    枚举的使用总结
    IIS WEB程序如何访问共享目录
    AngularJS之页面跳转Route
    ASP.NET MVC4 BundleConfig的注意事项
    Android自动化测试------monkey自定义脚本(四)
    Android自动化测试------monkey(三)
    Android自动化测试------monkey(二)
    Android自动化测试------monkey(一)
    (一)adb命令的使用
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/15439203.html
Copyright © 2011-2022 走看看