闲话Vuex

举报
民工 发表于 2020/09/13 17:13:31 2020/09/13
【摘要】 前言:为什么要Vuex在Vue项目中, 功能组件和服务组件的封装都会有各种各样的数据传递,用 props 定义字段或者是子组件 emit 来通信。但是,当我们的项目的复杂度逐渐增长的时候,组件会越来越多,而且一些组件并不存在调用关系,一些数据需要共享的时候,那么问题就来了:传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力;采用父子组件直接引用或者通过事件来变更和...

前言:为什么要Vuex


在Vue项目中, 功能组件和服务组件的封装都会有各种各样的数据传递,用 props 定义字段或者是子组件 emit 来通信。但是,当我们的项目的复杂度逐渐增长的时候,组件会越来越多,而且一些组件并不存在调用关系,一些数据需要共享的时候,那么问题就来了:


  • 传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力;

  • 采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝,代码冗余,慢慢的难以维护。


所以我们在项目中经常是全局定义一个实例“全局单例模式”,当然你也可以定义多个实例来共享一个数据源。


比如说我们有一个系统,包含左侧菜单组件以及右侧内容区域组件两部分。现在需要在内容区域给系统做一个全屏的功能,即点击右侧按钮时菜单隐藏。如果我们不使用Vuex,首先我们要在内容组件中使用 $emit 改变父组件的内容,然后将改变后的值传递左侧菜单组件控制隐藏。而现在我们有了 Vuex,菜单组件和内容组件共享某个状态变量,需要隐藏时只需要在内容组件中改变状态,即可在菜单组件中共享到相应的变量。


Vuex 就是为了解决这些问题而生。


Vue核心


state:驱动应用的数据源,类似于 vue 中的 data,是一棵单一状态树,涉及到的状态变量都存放在 state 上。


getter:store 的计算属性。类似于 vue 中的 computed 计算属性。当有多个组件对某个状态引用后过滤时,需要在多个组件内部使用 computed 进行计算,但有了 Getter 后我们可以将这个计算提前到 Vuex 中,一次计算多处使用。例如下面的代码我们要过滤出启用(状态为 1)状态的数据:



mutation:类似于 vue 中的方法,当我们需要改变 Vuex 中的某个变量时需要用到。


例如我们前面提到的显示隐藏菜单:



一般 mutations 中方法接受的第一个参数为 state,后面的参数则是传递的值。例如我们需要显示菜单则在组件中调用:


this.$store.commit('onCollapse'true);


这里改变值需要通过 commit(‘方法名’, ’…参数’)调用。


Action:  类似于 mutation,但是改变状态时只能通过提交 mutation,而不能直接改变。同时 action 可以包含异步的操作, 而 mutation 是不允许包含异步操作的。



Vuex 模块分割


由于 state 使用的是单一状态树,在项目较大时会导致 Vuex 变得臃肿且不易维护,这时候我们需要对项目进行分模块化处理,每一个模块都有自己的 state、mutation、action、getter:


Modules

 - app.js

 - user.js

index.js


最后统一在 inde.js 中导出:


import Vue from 'vue'

import vuex from 'vuex'


Vue.use(vuex);


import app from './modules/app'

import user from './modules/user'


const store = new vuex.Store({

    modules: {

        app: app,

        user: user

    }

})


export default store


Vuex 辅助函数


当我们的项目调用比较简单时辅助函数可能不会起到很大的作用,但当项目调用复杂时,这些辅助函数(mapState、mapGetters、mapMutations、mapActions)能大大提高开发效率,使得代码更加简洁优雅。


其中 mapState、mapGetters 是对变量的获取,放在组件的 computed 计算属性中即可,如下:


computed: {

        ...mapState({

    themeColorstate => state.app.themeColor,

    sysNamestate => state.app.appName,

    appHomeRouterstate => state.app.appHomeRouter,

})

}

而 mapMutations、mapActions 是对方法的调用,需要放在组件的 method 中,如下:


methods: {

        ...mapMutations({

    changeStatus: 'changeStatus'

}),

        changeStatus(){

        this.changeStatus()//直接调用 mapMutations 中的方法

        // this.$store.commit('changeStatus')//不使用辅助函数时的调用

    },

        ...mapActions({

        numAdd: 'actionNumAdd'

    }),

        actionnum6(){

        this.numAdd()//直接调用 mapActions 中的方法

        // this.$store.dispatch('actionNumAdd')//不使用辅助函数时的调用

    },

}


情景案例


有时候我们终于完成了一份试卷,可能由于手滑不小心把浏览器给关闭或者刷新了一下,导致辛辛苦苦做完的题目又得重头来过,而 Vuex 结合 Map 同样可以解决这个问题,核心思想就是将已经做过的题目更新到 Vuex 中。


讲到这里肯定会有人存在疑问:Vuex 中存储的状态变量在浏览器关闭或者刷新时会重新初始,这样如何能在浏览器刷新的时候获取到存储的值呢?这里我们需要引进缓存——sessionStorage。


首先在 state 中定义一个 answerMap:


state: {

    answerMap: sessionStorage.getItem("answerMap")

}

然后在每次做题完成后将答案存储到 sessionStorage 中,即:


const mutations = {

    setAnswerMap(stateanswer) {

        let map = JSON.parse(state.answerMap);

        if (map == null || !(map instanceof Map)) {

            map = new Map();

        }

        map.set(answer.id, answer);

        sessionStorage.setItem("answerMap", JSON.stringify(map))

    },

}

正常情况下到这里就结束了,但是实际操作后会发现每次获取到的 answerMap 总是为 null,原因在于最后一行代码:


sessionStorage.setItem("answerMap", JSON.stringify(map))

不能直接将 map 通过 JSON.stringify() 进行转换,需要先将 map 转成 obj 对象后再转成 json 对象,将 map 转成 obj:


let obj = Object.create(null);

for (let [k, v] of map) {

    obj[k] = v;

}

此时在获取则解决为 null 的情况,完善后代码如下:


const mutations = {

    setAnswerMap(stateanswer) {

        let map = JSON.parse(state.answerMap);

        if (map == null || !(map instanceof Map)) {

            map = new Map();

        }

        map.set(answer.id, answer);

        let obj = Object.create(null);

        for (let [k, v] of map) {

            obj[k] = v;

        }

        sessionStorage.setItem("answerMap", JSON.stringify(obj))

    },

}

这里借助了 Vuex + map + sessionStorage 防止已做题目丢失的情况,当然只是提供一种解决问题的思路,可能在真实场景中会更加复杂,需要根据不同的场景灵活运用。比如说我们如果需要在手动清除浏览器缓存、甚至服务器奔溃后依旧能获取到已做的题目则使用 Vuex 明显不合适,此时就需要利用 Redis 缓存来处理。


在实际项目中可能会遇到更加复杂的情况,但是万变不离其宗,在掌握基础的情况下总会有一种思路能够更好的解决问题。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。