闲话Vuex

网友投稿 660 2022-05-29

前言:为什么要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({

themeColor: state => state.app.themeColor,

sysName: state => state.app.appName,

appHomeRouter: state => 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(state, answer) {

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(state, answer) {

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

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

map = new Map();

闲话Vuex

}

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 缓存来处理。

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

Vue 数据结构

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SCSS 文件里的感叹号用法 - 给变量设置默认值
下一篇:一路绿灯、畅通无阻,网联汽车是一种什么体验
相关文章