前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue 之 Vuex 详细讲解

Vue 之 Vuex 详细讲解

作者头像
Java技术编程
发布2020-05-25 16:36:11
1.2K0
发布2020-05-25 16:36:11
举报
文章被收录于专栏:Java技术编程Java技术编程

前言

在上篇文章 从零搭建 Vue 开发环境 中,学习了 Vue 的语法,如何使用 Vue 进行开发,学会了如何搭建开发环境,打包部署等;文章中也介绍了兄弟组件之间传值是通过 Vuex 来实现的,只不过是进行了简单的应用,今天就来详细的深入学习如何使用 Vuex 来进行状态管理。

简介

Vuex 是专门为 Vue.js 设计的状态管理库,它集中存储,管理所有组件的状态;通过上篇文章的学习,我们知道父组件要把值传递给子组件的时候,可以通过 props 来传递,子组件要把值传递给父组件的时候,可以通过事件的形式来实现,而对于兄弟组件来说,就需要用到 Vuex 来实现了。也就是一个组件把值放入到 Vuex 中,另一个组件从中取值从而实现参数传递的效果。

一个应用中,Vuex 用一个 state 对象就包含了全部应用层级的状态,它作为一个“唯一数据源 (SSOT)”而存在。也就是说,每个应用将仅仅包含一个 store 实例。所以它应该是一个全局单例模式。

如何使用

首先要执行 npm install vuex --save 命令安装 Vuex 然后在 src 下创建 store 文件夹,在 store 文件夹内创建 index.js 文件,就在 index.js 里写 Vuex 。

最后在 main.js 中进行注册即可。

代码语言:javascript
复制
import Vue from 'vue'
import App from './App.vue'
import store from './store'

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

一个完整的 store 的 index.js 文件如下:

代码语言:javascript
复制
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
  // 所有的状态
  state: {
    userName: '张三',
    userAge: 2
  },

  // 改变状态的操作
  mutations: {
    setUserName(state, aUserName) {
      state.userName = aUserName;
    },
    setUserAge(state, aUserAge) {
      state.userAge = aUserAge;
    },
  },

  // 相等于 vue 的计算属性
  getters: {
    getAgeOfNextYear(state) {
      return state.userAge += 1;
    }
  },

  // 和 mutations类似, 支持异步操作
  actions: {
    getAgeOfNextYear(context) {
     context.commit('getAgeOfNextYear');
    }
  }
})

export default store // 需要在 main.js 中注册

一个完整的 store 的 index.js 文件主要有 state, mutations, gettersactions 4项内容,下面依次来说说每个项的作用和用法。

state

state 用来存放所有组件的状态,每个组件都可以读取和修改状态;但是,并不是每个状态都适合放在 state 里面,如果是单个组件私有的状态,最好还是作为组件的局部状态。放在 state 里面的状态一般是多个组件共享的。

代码语言:javascript
复制
const store = new Vuex.Store({
  // 所有的状态
  state: {
    userName: '张三',
    userAge: 2,
    address: '四川省成都市',
    job: 'Java'
  },

放在state里面的状态,在组件中,怎么获取值呢,是通过 this.$store.state.xxx 来获取的,如下所示,获取这几个 state 的值,并输出到控制台上:

代码语言:javascript
复制
export default {
    data() {
        return {
            userName: this.$store.state.userName,
            userAge: this.$store.state.userAge,
            address: this.$store.state.address,
            job: this.$store.state.job,
        }
    },
    created() {
        console.info(this.userName);
        console.info(this.userAge);
        console.info(this.address);
        console.info(this.job);
    }
}

控制台输出如下:

如果安装了 Vue 的 chrome 开发插件 Vue Devtools ,也可以看到 state 里面的状态值:

Getter

在 Vuex 中,Getter 的作用类似于 Vue 的计算属性的概念,可以对 state 里面的值进行计算,从而在组件调用的时候,不用每个组件都要重新计算,有点像 Java 里面的公共方法一样。

Getter里面的方法的第一个参数必须为 state

比如,我们要计算 state 里面的 userAge 这个状态值,让它返回明年的年龄:

代码语言:javascript
复制
const store = new Vuex.Store({
  // 所有的状态
  state: {
    userName: '张三',
    userAge: 2,
    address: '四川省成都市',
    job: 'Java'
  },

  getters: {
    getAgeOfNextYear(state) {
      return state.userAge += 1;
    }
  },

在组件中通过 this.$store.getters.xxx 来调用,如下所示 :

代码语言:javascript
复制
export default {
    data() {
        return {
            userAge: this.$store.state.userAge,
            ageOfNextYear: this.$store.getters.getAgeOfNextYear,
        }
    },
    created() {
        console.info(this.userAge);
        console.info(this.ageOfNextYear);
    }
}

控制台和 Devtools 输出如下:

可以看到,执行了 Getter 里面的方法之后,state 里面的值也改变了。

Mutation

在组件中通过 this.store.state.xxx 来获取状态的值,但是怎么改变它的值呢?是不能直接通过 this.store.state.xxx = XXX 来改变状态值的,而是需要通过 Mutation 来改变的。

代码语言:javascript
复制
const store = new Vuex.Store({
    state: {
        userName: '张三',
        userAge: 2,
        address: '四川省成都市',
        job: 'Java'
    },

    mutations: {
        setUserName(state, aUserName) {
            state.userName = aUserName;
        },
        setUserAge(state, aUserAge) {
            state.userAge = aUserAge;
        },
    },

在组件中通过 this.$store.commit('xxx', 'param') 来调用:

代码语言:javascript
复制
export default {
    data() {
        return {
            userName: this.$store.state.userName,
            userAge: this.$store.state.userAge,
        }
    },
    created() {
        console.info(this.userName);
        console.info(this.userAge);

        // 改变名字和年龄
        this.$store.commit('setUserName', '李四') 
        this.$store.commit('setUserAge', 5) 

        console.info(this.$store.state.userName);
        console.info(this.$store.state.userAge);
    }
}

控制台输出如下,可以看到改变已经生效了:

Vuex 的 store 中的状态是响应式的,也就是说当我们变更状态时,监视状态的 Vue 组件也会自动更新。 还有一点需要注意的是 Mutation 中的操作是同步的。

Action

Action 类似于 mutation,也是用来改变 state 中的状态值,不同的地方在于:

  1. Action 提交的是 mutation,而不是直接变更状态。
  2. Action 可以包含任意异步操作。
代码语言:javascript
复制
mutations: {
    setUserName(state, aUserName) {
        state.userName = aUserName;
    },
    setUserAge(state, aUserAge) {
        state.userAge = aUserAge;
    },
},

actions: {
    setUserName(context) {
        context.commit('setUserName');
    },
    setUserAge(context) {
        context.commit('setUserAge');
    }
}

调用: this.$store.dispatch('xxx')

Module

上面说了所有组件的状态都需要放在 state 中,试想一下,如果有很多状态需要维护,把所有的状态都放在 state 中,是不是不好维护?就像 Java 中也不会把所有的类写在一个包下,也是分包存放的,这样有利于阅读和维护。

Vuex 提供了 Module的概念,我们可以把 store 分割成多个模块(Module),每个 Module 都有自己的 state, mutations, gettersactions;这个 Module 可以根据功能模块来划分,也可以根据业务需求来划分。

代码语言:javascript
复制
const userManagerModule = {
    state: {
        userId: '11111',
        userName: '张三',
        userAge: 2,
    },
    mutations: {
        setUserName(state, aUserName) {
            state.userName = aUserName;
        },
        setUserAge(state, aUserAge) {
            state.userAge = aUserAge;
        },
    },
    getters: {},
    actions: {}
}

const goodsManagerModule = {
    state: {
        goodsName: '衣服',
        goodsPrice: 3,
    },
    mutations: {
        setUserName(state, aUserName) {
            state.userName = aUserName;
        },
        setUserAge(state, aUserAge) {
            state.userAge = aUserAge;
        },
    },
    getters: {},
    actions: {}
}
const store = new Vuex.Store({
    modules: {
        userManager: userManagerModule,
        goodsManager: goodsManagerModule
    }
})

export default store // 需要在 main.js 中注册

然后通过 this.$store.state.moduleName.xxx 来获取对应 Module 的状态。

代码语言:javascript
复制
    export default {
        data() {
            return {
                userName: this.$store.state.userManager.userName,
                userAge: this.$store.state.userManager.userAge,
                goodsName: this.$store.state.goodsManager.goodsName,
                goodsPrice: this.$store.state.goodsManager.goodsPrice,
            }
        },
        created() {
            console.info(this.userName);
            console.info(this.userAge);
            console.info(this.goodsName);
            console.info(this.goodsPrice);
        }
    }

改变对应 Module 的状态值还是通过 this.$store.commit('xxx', 'param') 来实现:

代码语言:javascript
复制
export default {
    data() {
        return {
            userName: this.$store.state.userManager.userName,
            userAge: this.$store.state.userManager.userAge,
            goodsName: this.$store.state.goodsManager.goodsName,
            goodsPrice: this.$store.state.goodsManager.goodsPrice,
        }
    },
    created() {
        console.info(this.userName);
        console.info(this.userAge);
        this.$store.commit('setUserName', '李四');
        this.$store.commit('setUserAge', 5);
        console.info(this.$store.state.userManager.userName);
        console.info(this.$store.state.userManager.userAge);


        console.info(this.goodsName);
        console.info(this.goodsPrice);
        this.$store.commit('setGoodsName', '裤子');
        this.$store.commit('setGoodsPrice', 6);
        console.info(this.$store.state.goodsManager.goodsName);
        console.info(this.$store.state.goodsManager.goodsPrice);
    }
}

可以看到 state 的状态都已经改变了。

但是如果多个 Module 之间的 mutations 中有同名的方法,执行 this.$store.commit('xxx', 'param') 后,哪个 Module 会生效呢?接下来看个例子:

代码语言:javascript
复制
const userManagerModule = {
    state: {
        userId: '11111',
        userName: '张三',
        userAge: 2,
    },
    mutations: {
        setUserId(state, userId) {
            state.userId = userId;
        },
    },
}

const goodsManagerModule = {
    state: {
        userId: '22222',
        goodsName: '衣服',
        goodsPrice: 3,
    },
    mutations: {
        setUserId(state, userId) {
            state.userId = userId;
        },
    },
}

现在两个 Module 都有 userId 属性,且分别为 1111122222,此外,都有改变 userId 的方法 setUserId,控制台输出如下所示:

代码语言:javascript
复制
export default {
    data() {
        return {
            userManagerModuleUserId: this.$store.state.userManager.userId,
            goodsManagerModuleUserId: this.$store.state.goodsManager.userId,
        }
    },
    created() {
        console.info(this.userManagerModuleUserId);
        console.info(this.goodsManagerModuleUserId);
    }
}

现在来改变 userId 的值为 88888,之后再次输出:

代码语言:javascript
复制
export default {
    data() {
        return {
            userManagerModuleUserId: this.$store.state.userManager.userId,
            goodsManagerModuleUserId: this.$store.state.goodsManager.userId,
        }
    },
    created() {
        console.info(this.userManagerModuleUserId);
        console.info(this.goodsManagerModuleUserId);

        this.$store.commit('setUserId', '88888');

        console.info(this.$store.state.userManager.userId);
        console.info(this.$store.state.goodsManager.userId);
    }
}

可以看到每个 Module 的 userId 都变了。

所以,分了 Module 之后,还需要注意这点,不然一个 Module 不小心修改了之后,出现问题了都不好排查。 这是因为默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

要解决这个问题,就需要使用命名空间 namespaced 来实现,在创建 Module 的时候,把 namespaced属性设置为 true,这样当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名

如下所示:

代码语言:javascript
复制
const userManagerModule = {
    namespaced:true,
    state: {
        userId: '11111',
        userName: '张三',
        userAge: 2,
    },
    mutations: {
        setUserId(state, userId) {
            state.userId = userId;
        },
    },
}

const goodsManagerModule = {
    namespaced:true,
    state: {
        userId: '22222',
        goodsName: '衣服',
        goodsPrice: 3,
    },
    mutations: {
        setUserId(state, userId) {
            state.userId = userId;
        },
    },
}

此时 state 的值为:

现在来改变每个 Module 的 userId 的值,通过

this.$store.commit('moduleName/functionName', 'param') 来实现。

代码语言:javascript
复制
export default {
    data() {
        return {
            userManagerModuleUserId: this.$store.state.userManager.userId,
            goodsManagerModuleUserId: this.$store.state.goodsManager.userId,
        }
    },
    created() {
        console.info('修改之前 userManager模块的 userId = ' + this.userManagerModuleUserId);
        console.info('修改之前 goodsManager模块的 userId = ' + this.goodsManagerModuleUserId);

        // 改变 userManager 模块下的 userId 的值
        this.$store.commit('userManager/setUserId', '44444');
        // 改变 goodsManager 模块下的 userId 的值
        this.$store.commit('goodsManager/setUserId', '88888');

        console.info('修改之后 userManager模块的 userId = ' + this.$store.state.userManager.userId);
        console.info('修改之后 goodsManager模块的 userId = ' + this.$store.state.goodsManager.userId);
    }
}

控制台输出如下:

可以看到,现在每个 Module 只能修改自己内部的状态了。

通过使用命名空间 namespaced 就可以解决多个 Module 下 mutations 同名的问题啦。

同样,多个 Module 下的 Getter, Action 下的同名方法也是如此

代码语言:javascript
复制
Action:
    dispatch('moduleName/functionName')
Getter:
    getters['moduleName/functionName']

总结

到这里 Vuex 的学习就结束啦,由于之前项目时间紧,大概学会了如何用 Vuex 就直接上手写代码了,没有深入了解,只是简单的 state 和 mutations,也没有分 Module,后面的业务中由于需要加更多的渠道进来,那后面就需要根据渠道来分 Module 了,每个渠道管理自己的状态。

此外,还了解了多个 Module 下同名的 mutations, Getter, Action 的处理方式,也算是避免了一个坑。

看来学习新知识还是要花时间去深入学习,才能写出易于维护,高质量的代码呀。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java技术大杂烩 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 简介
  • 如何使用
  • state
  • Getter
  • Mutation
  • Action
  • Module
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档