Vuex怎么学(二)

Vuex怎么学(二)

  • 一个项目做完了,回头再来看看vuex这个坑,以加深对vuex的印象,如果你有兴趣,那么一起来看吧~~

坑一:vuex公共状态管理与vuex状态渲染机制的坑

  • 先来看一段代码,理论上,count1(第一个p标签的值)和count2(第二个p标签的值)显示的数字应该是相同的,但实际上,只有count2能保持实时刷新
  • 这里分析下原因:
    1. count1是用组件内声明的变量去接收vuex内的公共状态,页面首次渲染可以成功得到state中的状态,但后续就不再去state中拿数据了。这里在data中接收公共状态的数据,相当于是个一次性赋值的操作
    2. count2直接在{{}}中调用state中的数据,当state中的数据发生改变,count2必然会发生改变,这样就实现了当前数据与公共状态中的数据保持一致
    // store.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    import axios from 'axios'
    Vue.use(Vuex);
    const store = new Vuex.Store({
        state: {
            count: 10,
            secCount: 10086
        },
        getters: {
            // getter 返回一个函数 add(num)
            add: (state, getter) => (num) => {
                return state.count + num;
            },
            // getter 返回一个数值 app
            app: (state, getter) => {
                return state.count - 10;
            }
        },
        mutations: {
            plus(state, num) {
                state.count += num;
            },
            minus(state, num) {
                state.count -= num;
            },
            printf(state, res) {
                console.log('调用mutions中的printf方法打印: ', res);
            }
        },
        actions: {
            actionA({ dispatch, commit }) {
                setTimeout(function () {
                    console.log('延时1s打印actionA')
                }, 1000);
            },
            actionB({ dispatch, commit }) {
                axios.get('../../static/data.json').then((res) => {
                    console.log('成功获取数据: ');
                    commit('printf',res.data);
                }).catch((err) => console.log('失败了!',err));
            }
        }
    });
    export default store;  

    // demo01.vue
    <template>
    <div class="hello">
        <p>count1: {{ count }}</p>
        <p>count2: {{ $store.state.count }}</p>
        <button @click="plus">点击+2</button>
    </div>
    </template>
    <script>
    export default {
        data () {
            return {
            count: this.$store.state.count
            }
        },
        methods:{
            plus(){
                console.log('点击+2');
                this.$store.commit('plus', 2);
            }
        }
    }
    </script>
  • 点击几次增加数字的效果如下
    渲染机制的问题
  • 这里提醒朋友们注意不要因为这个不起眼的细节导致你的vuex状态达不到效果
  • 那有朋友就问啦,没有办法使count1的形式也达到实时渲染公共状态的效果呢?毕竟项目大的话,每次都要写$store.state.XXX是一件很麻烦的事情,也很难看!!!这里我告诉你,有办法, 向下看!!!

先补一个ES6的新东西,以免新人看下面的坑不明所以

  • 我们使用Vue开发项目一般都是用webpack快速构建,好处是可以无后顾之忧的使用ES6和ES7的新语法,新语法有很多,这里我只说两个,便于大家看后边的东西,其余的在后边的学习工作中在慢慢以博客形式更新
  • 第一个:ES6的promise异步处理。下面的实例中,用到了一个轻量级的ajax库axios,它请求完成返回的就是一个promise对象,promise对象只有三种状态(异步执行中pending,执行成功resovled,执行失败rejected),并且在同一时刻只可能处于其中的一种状态。在axios中,执行成功会调用then()方法,失败会被catch()方法捕捉
    axios.get('../../static/data.json')     // pending 执行中的promise
    .then((res) => {                        // resovled 执行成功
        console.log('成功获取数据: ');
        commit('printf',res.data);
    })
    .catch((err) => console.log('失败了!',err));   // rejected 执行失败
  • 第二个:扩展运算符...对象或数组。作用是不使用遍历、contact方法等,就可以快捷的将数组(或对象)混入到另一个或多个数组(或对象中),例如
    let ar1 = [1, 3], ar2 = ['a', 'b'];
    let arr = ['abc', ...ar1, ...ar2, 456];
    console.log(arr);   // ['abc', 1, 3, 'a', 'b', 456]

    let obj1 = {a: 1, b: 3}, obj2 = {aaa: 345};
    let obj = {...obj1, qqq: 'aqa', ...obj2, aaa: 0};
    console.log(obj);    // {a: 1, b: 3, qqq: 'aqa', aaa: 0} 注意,这里的aaa的值被后面的覆盖为0

坑二:vuex的map辅助函数:mapState 、mapGetters、mapMutations、mapActions

  • 说起来呀,这个坑还真的是个磨人的老妖精呐。
  • 学习了一阵子ES6的我天真的以为这里的mapXXX跟ES6的对象的map方法一样,官方文档也写得太简单并且没有案例,折腾了老半天,一无所获-,-|
  • 我又继续往下看,打算看完还是不懂就放弃,反正不用这mapXXX也可以。但是,当我看到mapMutations和mapActions时,突然看到了这么一句,豁然开朗,这里的mapXXX不是简单的对象关系映射,而是用来映射对应的状态到vue组件中,作用主要是用来精简代码,将store中的状态映射到组建中,绑定到该组件的Vue对象上(this指针),例如下面的this.increment()相比this.$store.dispatch('increment')少了好多个字母是吧(发现新大陆,满满的成就感:)
    ...mapActions([
        'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
        // `mapActions` 也支持载荷:
        'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
  • 举一反三,我们自己来练习下这个mapXXX辅助函数
    // store.js 还是上面那个
    // demo1.vue 稍作改变
    <template>
    <div class="hello">
        <p>count1: {{ count }}</p>
        <p>count2: {{ $store.state.count }}</p>
        <button @click="plus">点击+2</button>
    </div>
    </template>
    <script>
    import {mapState} from 'vuex'
    export default {
        data () {
            return {}
        },

        computed:{
            ...mapState({
                count: 'count'
            })
        },

        methods:{
            plus(){
                console.log('点击+2');
                this.$store.commit('plus', 2);
            }
        }
    }
    </script>
  • 这里我先用一下mapState这个辅助函数,因为state是用来保存数据的,在组件中对应的就是computed计算属性了,因此我们在computed中映射一个count变量来接收state中的count变量,当然我们可以在这里给count1的变量任意命名,例如computed: mapState({ count1: 'count' }),,这样在用{{ count1 }}就可以接收到映射过来的state中的公共状态了,同时也会保持实时渲染。<p>count1: {{ count }}</p>现在的渲染效果与<p>count2: {{ $store.state.count }}</p>的渲染效果一样了,很神奇吧~~

  • 循序渐进,我们来看一下mapGetter的使用

    // demo1.vue 稍作改变
    <template>
    <div class="hello">
        <p>count1: {{ count }}</p>
        <p>count2: {{ $store.state.count }}</p>
        <p>app1: {{ app }}</p>
        <p>app2: {{ $store.getters.app }}</p>
        <button @click="plus">点击+2</button>
    </div>
    </template>
    <script>
    import {mapState} from 'vuex'
    export default {
        data () {
            return {}
        },

        computed:{
            ...mapState({
                count: 'count'
            }),
            ...mapGetters([
                'app'
            ])
        },

        methods:{
            plus(){
                console.log('点击+2');
                this.$store.commit('plus', 2);
            }
        }
    }
    </script>
  • 这样我们的getters就被映射到了demo1.vue组件中,就跟在组件中使用自己的状态(变量)一样方便了
  • 这里细心地同学就会发现,store.js中的getters有一个返回的是一个函数,不是数值能不能放到计算属性中呢?试一试吧
    <template>
    <div class="hello">
            <p>count1: {{ count }}</p>
            <p>count2: {{ $store.state.count }}</p>
            <p>app1: {{ app }}</p>
            <p>app2: {{ $store.getters.app }}</p>
            <p>add1: {{ add(num) }}</p>
            <p>add2: {{ $store.getters.add(num) }}</p>
        <button @click="plus">点击+2</button>
    </div>
    </template>

    <script>
    import {mapState,mapGetters} from 'vuex'
    export default {
        data () {
            return {
                num: 100
            }
        },
            computed:{
                ...mapState({
                    count: 'count'
                }),
                ...mapGetters([
                    'app',
                    'add'
                ])
            },
        created(){
            console.log('getters ', this.$store.getters.add(20));
        },
        methods:{
            plus(){
                console.log('点击+2');
                this.$store.commit('plus', 2);
            }
        }
    }
    </script>
  • 运行结果完全没问题,还可以传入参数
    运行mapGetters_02

  • mapMutations和mapActions的使用

     // demo1.vue
    <template>
        <div class="hello">
            <p>count1: {{ count }}</p>
            <p>count2: {{ $store.state.count }}</p>
            <p>app1: {{ app }}</p>
            <p>app2: {{ $store.getters.app }}</p>
            <p>add1: {{ add(num) }}</p>
            <p>add2: {{ $store.getters.add(num) }}</p>
            <button @click="plus">点击+2</button>
            <br><br>
            <button @click="getData">读取json</button>
        </div>
    </template>

    <script>
    import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
    export default {
        data () {
            return {
                num: 100
            }
        },
        computed:{
            ...mapState({
                count: 'count'
            }),
            ...mapGetters([
                'app',
                'add'
            ])
        },
        methods:{
            //   ...mapMutations([
            //       'plus'
            //   ]),
            ...mapMutations({
                newNamePlus: 'plus'
            }),
            ...mapActions([
                'actionA',
                'actionB'
            ]),
            plus(){
                console.log('点击+2');
                //   this.$store.commit('plus', 2);
                /**
                * 注意!!!严重错误
                * 
                * 因为我们的store.mutations中有个'plus'方法,上面注释掉的mapMutations映射过来的时候,
                * 方法名与本组件内的点击事件'plus()'方法同名,在下面直接调用(已被注释掉)会出现死循环,在成内存泄漏,继而使浏览器卡死的问题
                * 
                * 所以这里需要注意,使用上面为注释掉的mapMutations映射方法,
                * 如有同名函数方法在映射时一定要给方法重新命名
                */
                // this.plus(2);
                this.newNamePlus(2); // 实现效果与this.$store.commit('plus', 2);完全相同
            },
            getData(){
                this.actionB();// 作用等同与没有使用mapActions映射时的this.$store.dispatch('actionB');
            }
        }
    }
    </script>
  • 这里也有个大坑,有的时候 会出现命名重复的问题,不使用mapXXX方法时因为都在各自的文件中被调用,不会出现互相影响的问题,但是映射到同一个组件中时就可能出现上面的问题,造成程序出现内存泄漏的bug
  • 至于mapMutations和mapActions的映射一样,都是两种方法:直接映射的话,使用...mapActions(['方法1','方法2'])即可,如果映射过来时需要重新命名,那就需要用...mapActions({ newName1: '方法1', newName2: '方法2'})

最后,vuex在很小的项目中是显得有些冗余的,中型项目中,简单的vuex功能就可以很好的实现大多数功能了,大型项目中,必须严格的使用vuex的各部分,做到条理清晰,才能使团队开发高效的进行

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值