为什么会出现Vuex

非父子关系的组件如何进行通信?(Event Bus)
bus.js


  1. import Vue from 'vue';
  2. export default new Vue();

foo.vue


  1. import bus from './bus.js';
  2. export default {
  3. methods: {
  4. changeBroData() {
  5. bus.$emit('changeBarData');
  6. }
  7. }
  8. }

bar.vue


  1. import bus from './bus.js';
  2. export default {
  3. created() {
  4. bus.$on('changeBarData',() => {
  5. this.count++;
  6. });
  7. }
  8. }

查看效果

但是当我们需要修改这个操作的时候,我们需要动3个地方,倘若项目小的话还倒好说,但是对于大项目组件间交互很多的概况,Event Bus就会表现的很吃力。Vuex的出现就是为了解决这一状况。

Vuex介绍

安装:
script标签引入
https://unpkg.com/vuex
NPM
npm install vuex --save-dev


  1. import Vue from 'vue';
  2. import Vuex from 'vuex';
  3. Vue.use(Vuex);

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex的状态存储是响应式的。当Vuex的状态属性发生变化时,相应的组件也会更新。
  2. 无法直接修改Vuex的状态。只能通过显式的提交(commit)。

简单的Store


  1. const store = new Vuex.Store({
  2. state: {
  3. count: 0
  4. },
  5. mutations: {
  6. increase(state) {
  7. state.count++;
  8. }
  9. }
  10. });
  11. store.commit('increase');
  12. console.log(store.state.count); // 1

State

从上文我们知道Vuex是响应式的,我们如何在Vue实例中使用Vuex中的实例呢,自然离不开计算属性computed了。

在 Vue 组件中获得 Vuex 状态


  1. //仓库
  2. const store = new Vuex.Store({
  3. state: {
  4. count: 1
  5. }
  6. });
  7. //foo组件
  8. const foo = {
  9. template: `
  10. <div>
  11. {{ count }}
  12. <div>
  13. `,
  14. computed: {
  15. count: () => store.state.count
  16. }
  17. };

这时候问题就来了,如果这样进行引入store的话,组件就会以来全局状态单例。
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):


  1. //挂载App
  2. new Vue({
  3. el: '#app',
  4. //注入store选项
  5. store,
  6. render: h => h(App)
  7. });
  8. //foo组件
  9. const foo = {
  10. template: `
  11. <div>
  12. {{ count }}
  13. <div>
  14. `,
  15. computed: {
  16. count() {
  17. // 不能使用箭头函数,不然this就不是Vue实例了
  18. // 通过this.$store获取到store实例
  19. return this.$store.state.count
  20. }
  21. }
  22. };

如果有很多状态需要映射,我们岂不是要写好多代码,这时候需要用到mapState辅助函数,使用 mapState 辅助函数帮助我们生成计算属性。
辅助函数 实例1


  1. const foo = {
  2. template: `
  3. <div>
  4. count: {{ count }}
  5. countAlias: {{ countAlias }}
  6. countPlusLocalCount: {{ countPlusLocalCount }}
  7. <div>
  8. `,
  9. data() {
  10. return {
  11. localCount: 2
  12. };
  13. },
  14. computed: Vuex.mapState({
  15. //只处理仓库中的count
  16. count: state => state.count*2,
  17. //映射
  18. countAlias: 'count',
  19. //需要用到Vue实例的话不能使用箭头函数不然this无法获取到Vue实例
  20. countPlusLocalCount(state) {
  21. return state.count + this.localCount;
  22. }
  23. })
  24. };

当然当映射名与state中属性名相同时,可以通过mapState(['count'])这种形式书写。
因为组件本身也有许多计算属性直接使用mapState的话无法扩充computed了,这时候我们可以使用ES新属性: 对象展开运算符


  1. computed: {
  2. localComputed() {
  3. return this.count;
  4. },
  5. ...Vuex.mapState({
  6. //只处理仓库中的count
  7. count: state => state.count*2,
  8. //映射
  9. countAlias: 'count',
  10. //需要用到Vue实例的话不能使用箭头函数不然this无法获取到Vue实例
  11. countPlusLocalCount(state) {
  12. return state.count + this.localCount;
  13. }
  14. })
  15. }

ATT:使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

Getter

有时候我们需要过滤/处理state中的属性值,就像Vue实例中我们需要处理data数据一样,Vue有computed。Vuex同样提供给我们一个属性getter,getter就是Vuex的“计算属性”。
Getter接收state作为第一个参数


  1. //仓库
  2. const store = new Vuex.Store({
  3. state: {
  4. users: [{
  5. name: 'jason',
  6. id: 1,
  7. female: false
  8. }, {
  9. name: 'molly',
  10. id: 2,
  11. female: true
  12. }, {
  13. name: 'steven',
  14. id: 3,
  15. female: false
  16. }]
  17. },
  18. getters: {
  19. // 过滤所有属性中female是true的对象
  20. getFemaleUsers: state => state.users.filter(user => user.female)
  21. }
  22. });
  23. console.log(store.getters.getFemaleUsers);
  24. //[{ "name": "molly", "id": 2, "female": true }]

当然Getter同样有辅助函数 mapGetters将 store 中的 getter 映射到局部计算属性
直接上对象展开运算符了
辅助函数 实例1


  1. //foo组件
  2. const foo = {
  3. template: `
  4. <div>
  5. femaleList: {{ femaleList }}
  6. <div>
  7. `,
  8. computed: {
  9. // 属性名相同的不再阐述
  10. ...Vuex.mapGetters({
  11. femaleList: 'getFemaleUsers'
  12. })
  13. }
  14. };

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
提交时额外的参数被称为载荷
普通风格提交方式


  1. store.commit('mutationName', {
  2. params: '参数'
  3. });

对象风格提交方式


  1. store.commit({
  2. type: 'mutationName',
  3. params: '参数'
  4. });

注意点:

  1. 设置对象新属性时
    方法1:Vue.set(obj, 'newVal', 100)
    方法2:obj = {...obj, newVal: 100};
  2. Mutations的事件类型尽量使用常量,并用一个文件进行维护。方便协同开发以及维护。


    1. // 存储事件名mutations-types.js
    2. export const MUTATIONS_GETDATA = 'MUTATIONS_GETDATA';
    3. export const MUTATIONS_SUCCESS = 'MUTATIONS_SUCCESS';

    1. import { MUTATIONS_GETDATA ,MUTATIONS_SUCCESS } from 'path/mutations-types.js';
    2. const store = new Vuex.Store({
    3. mutations: {
    4. [MUTATIONS_GETDATA]() {
    5. // todo
    6. },
    7. [MUTATIONS_SUCCESS]() {
    8. // todo
    9. }
    10. }
    11. });
  3. Mutations中的函数必须都是同步函数,异步函数都要写在Actions中。

在组件中提交Mutation
$store.commit('xxx','yyy')
当然也有对应的辅助函数帮助我们映射到对应的methods方法中


  1. import { mapMutations } from 'vuex';
  2. export default {
  3. methods: {
  4. ...mapMutations([
  5. 'event1',
  6. 'event2'
  7. ]),
  8. ...mapMutations({
  9. eventAlias: 'event3' //将this.eventAlias()映射为this.$store.commit('event3')
  10. })
  11. }
  12. };

Action

Action与Mutation的不同在于Action不直接修改状态只是做commit一个mutation、然后就是可以做异步操作。
简单的Action


  1. const INCREASE_COUNT = 'INCREASE_COUNT';
  2. const store = new Vuex.Store({
  3. state: {
  4. count: 0
  5. },
  6. mutations: {
  7. [INCREASE_COUNT](state) {
  8. state.count++;
  9. }
  10. },
  11. actions: {
  12. add({ commit }) {
  13. commit(INCREASE_COUNT);
  14. }
  15. }
  16. });

看到这我们或许以为Actions的作用与Mutations不一样么,对到现在为止作用是一样的,但是Actions可以执行异步操作


  1. const INCREASE_COUNT = 'INCREASE_COUNT';
  2. const store = new Vuex.Store({
  3. state: {
  4. count: 0
  5. },
  6. mutations: {
  7. [INCREASE_COUNT](state) {
  8. state.count++;
  9. }
  10. },
  11. actions: {
  12. add({ commit }) {
  13. setTimeout(() => {
  14. commit(INCREASE_COUNT);
  15. }, 1000);
  16. }
  17. }
  18. });

Action的分发也有两种方式
普通风格分发方式


  1. store.dispatch('actionName', {
  2. params: '参数'
  3. });

对象风格分发方式


  1. store.dispatch({
  2. type: 'actionName',
  3. params: '参数'
  4. });

辅助函数mapActions用法与mapMutations无异
代码链接


  1. methods: {
  2. ...mapActions({
  3. add: 'add'
  4. })
  5. }

组合Action


  1. actions: {
  2. actionA ({ commit }) {
  3. return new Promise((resolve, reject) => {
  4. setTimeout(() => {
  5. commit('someMutation')
  6. resolve()
  7. }, 1000)
  8. })
  9. },
  10. // ...
  11. actionB ({ dispatch, commit }) {
  12. return dispatch('actionA').then(() => {
  13. commit('someOtherMutation')
  14. })
  15. }
  16. }
  17. store.dispatch('actionA').then(() => {
  18. // ...
  19. })
  20. 或者
  21. store.dispatch('actionB');

Module

如果项目庞大的话我们需要维护很多状态,这时候store会变得非常庞大,我们就需要store分割成很多模块(Module),每个模块同样拥有自己的state,getters,mutations,actions以及modules


  1. const moduleA = {
  2. state: {
  3. a: 'A'
  4. }
  5. };
  6. const moduleB = {
  7. state: {
  8. a: 'B'
  9. }
  10. };
  11. const store = new Vuex.Store({
  12. modules: {
  13. moduleA,
  14. moduleB
  15. }
  16. });
  17. console.log(store.state.moduleA.a); //A
  18. console.log(store.state.moduleB.a); //B
  1. 模块内部的mutationgetter接收的第一个参数都是局部状态对象


    1. 例如
    2. const moduleA = {
    3. state: {
    4. price: 50,
    5. count: 2
    6. },
    7. getters: {
    8. totalPrice: state => state.price*state.count
    9. },
    10. mutations: {
    11. decreaseCount(state) {
    12. state.count--;
    13. }
    14. }
    15. };
  2. 模块内部的getter,根节点的状态会在第三个参数展示出来。
    注:根节点的状态就是指的store.state


    1. const moduleB = {
    2. state: {
    3. count: 2
    4. },
    5. getters: {
    6. sum: (state, getters, rootState) => state.count+rootState.count
    7. }
    8. };
    9. const store = new Vuex.Store({
    10. state: {
    11. count: 1
    12. },
    13. modules: {
    14. moduleB
    15. }
    16. });
    17. console.log(store.getters.sum);// 3
  3. 模块内部的action,局部状态:context.state根部状态:context.rootState


    1. const moduleC = {
    2. state: {
    3. count: 2
    4. },
    5. mutations: {
    6. increaseCount(state) {
    7. state.count++;
    8. }
    9. },
    10. actions: {
    11. addCount({ commit, state, rootState }) {
    12. let sum = state.count + rootState.count;
    13. if(sum % 3 === 0) {
    14. commit('increaseCount');
    15. }
    16. }
    17. }
    18. };
    19. const store = new Vuex.Store({
    20. state: {
    21. count: 1
    22. },
    23. modules: {
    24. moduleC
    25. }
    26. });
    27. console.log(store.state.moduleC.count);// 2
    28. store.dispatch('addCount');
    29. console.log(store.state.moduleC.count);// 3

命名空间
默认情况下,模块内部的 actionmutationgetter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutationaction 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为命名空间模块。当模块被注册后,它的所有 getteractionmutation 都会自动根据模块注册的路径调整命名。(摘自官网)
注1:特别提出:模块内的state是嵌套的,namespaced属性不会对其产生丝毫影响。
注2:没加namespaced: true的模块会继承父模块的命名空间
代码链接


  1. const store = new Vuex.Store({
  2. modules: {
  3. moduleA: {
  4. namespaced: true,
  5. getters: {
  6. count: () => 0 // store.getters['moduleA/count']
  7. },
  8. modules: {
  9. moduleB: {
  10. getters: {
  11. count1: () => 1 //继承了父模块的命名空间 store.getters['moduleA/count1']
  12. }
  13. },
  14. moduleC: {
  15. namespaced: true,
  16. getters: {
  17. count2: () => 2 //继承了父模块的命名空间 store.getters['moduleA/moduleC/count1']
  18. }
  19. }
  20. }
  21. }
  22. }
  23. });
  24. console.log(store.getters['moduleA/count']);// 0
  25. console.log(store.getters['moduleA/count1']);// 1
  26. console.log(store.getters['moduleA/moduleC/count2']);// 2

在命名空间模块内部访问全局内容

模块内部希望使用全局state与全局getter

  1. rootStaterootGetters会作为getter第三个第四个参数传入


    1. someGetter: (state, getters, rootState, rootGetters) => {
    2. //todo
    3. }

    1. const store = new Vuex.Store({
    2. getters: {
    3. someOtherGetter: state => 'ROOT'
    4. },
    5. modules: {
    6. moduleA: {
    7. namespaced: true,
    8. getters: {
    9. someGetter(state,getters,rootState,rootGetters) {
    10. return getters.someOtherGetter; // INSIDE
    11. // return rootGetters.someOtherGetter; // ROOT
    12. },
    13. someOtherGetter: state => "INSIDE"
    14. }
    15. }
    16. }
    17. });
    18. console.log(store.getters['moduleA/someGetter']);
  2. 也会通过context.rootStatecontext.rootGetts传入


    1. someAction:(context) => {
    2. //todo
    3. }
    4. 或者
    5. someAction:({ commit, dispatch, rootState, rootGetters }) => {
    6. //todo
    7. }

模块内部希望使用全局mutation与全局action,只需要在执行分发或者提交的时候在第三个参数位置传入{ root: true }


  1. dispatch('someOtherAction', null, {root: true});
  2. commit('someOtherMutation', null, {root: true});

栗子(没有写mutation与state的可以自行尝试)


  1. const store = new Vuex.Store({
  2. getters: {
  3. someOtherGetter: state => 'ROOT'
  4. },
  5. actions: {
  6. someOtherAction() {
  7. console.log('ROOT_ACTION');
  8. }
  9. },
  10. modules: {
  11. moduleA: {
  12. namespaced: true,
  13. getters: {
  14. someGetter(state,getters,rootState,rootGetters) {
  15. return getters.someOtherGetter; // INSIDE
  16. // return rootGetters.someOtherGetter; // ROOT
  17. },
  18. someOtherGetter: state => "INSIDE"
  19. },
  20. actions: {
  21. someAction({ dispatch, getters, rootGetters }) {
  22. console.log(getters.someOtherGetter);//INSIDE
  23. console.log(rootGetters.someOtherGetter);//ROOT
  24. dispatch('someOtherAction');//INSIDE_ACTION
  25. dispatch('someOtherAction', null, {root: true});//ROOT_ACTION
  26. },
  27. someOtherAction() {
  28. console.log('INSIDE_ACTION');
  29. }
  30. }
  31. }
  32. }
  33. });
  34. console.log(store.getters['moduleA/someGetter']);
  35. store.dispatch('moduleA/someAction');

当我们将store里的状态映射到组件的时候,会出现下面的问题


  1. computed: {
  2. ...mapState({
  3. b1:state => state.moduleA.moduleB.b1,
  4. b2:state => state.moduleA.moduleB.b2
  5. })
  6. },
  7. methods: {
  8. ...mapActions({
  9. 'moduleA/moduleB/action1',
  10. 'moduleA/moduleB/action2'
  11. })
  12. }
  13. 是不是比较繁琐,代码太过冗余。

当然mapStatemapGettersmapMutationsmapActions这些辅助函数都可以将空间名称字符串作为第一个参数传递,这样上面的例子可以简化成


  1. computed: {
  2. ...mapState('moduleA/moduleB', {
  3. b1:state => state.b1,
  4. b2:state => state.b2
  5. })
  6. },
  7. methods: {
  8. ...mapActions('moduleA/moduleB', {
  9. 'action1',
  10. 'action2'
  11. })
  12. }

当然还有一个方法,使用Vuex提供的createNamespacedHelpers创建基于某个命名空间函数,它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数。


  1. import { createNamespacedHelpers } from 'vuex';
  2. const { mapState , mapActions } = createNamespacedHelpers('moduleA/moduleB');
  3. export default {
  4. computed: {
  5. ...mapState({
  6. b1:state => state.b1,
  7. b2:state => state.b2
  8. })
  9. },
  10. methods: {
  11. ...mapActions({
  12. 'action1',
  13. 'action2'
  14. })
  15. }
  16. }

模块重用
有时我们可能需要创建一个模块的多个实例:

  1. 创建多个 store,他们公用同一个模块
  2. 在一个 store 中多次注册同一个模块

如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。

实际上这和 Vue 组件内的 data 是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态


  1. const MyReusableModule = {
  2. state () {
  3. return {
  4. foo: 'bar'
  5. }
  6. },
  7. // mutation, action 和 getter 等等...
  8. }

原文地址:https://segmentfault.com/a/1190000012645384

Vuex-一个专为 Vue.js 应用程序开发的状态管理模式的更多相关文章

  1. Vue.js 2.x笔记:状态管理Vuex(7)

    1. Vuex简介与安装 1.1 Vuex简介 Vuex是为vue.js应用程序开发的状态管理模式,解决的问题: ◊ 组件之间的传参,多层嵌套组件之间的传参以及各组件之间耦合度过高问题 ◊ 不同状态中 ...

  2. Vue.js状态管理模式 Vuex

    vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 安装.使用 vuex 首先我们在 vue. ...

  3. 五、vue状态管理模式vuex

    一.vuex介绍 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 即data中属性同时有一 ...

  4. 转 理解vuex -- vue的状态管理模式

    转自:https://segmentfault.com/a/1190000012015742 vuex是什么? 先引用vuex官网的话: Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 ...

  5. vuex -- vue的状态管理模式

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 状态管理模式.集中式存储管理 一听就很高大 ...

  6. 理解Vue的状态管理模式Vuex

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 状态管理模式.集中式存储管理,一听就很高大 ...

  7. Vue状态管理模式---Vuex

    1. Vuex是做什么的? 官方解释: Vuex 是一个专为Vue.js 应用程序开发的 状态管理模式 它采用 集中式存储管理 应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化 ...

  8. Vuex(一)——vuejs的状态管理模式

    一.Vuex是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式. 它采用集中式存储 管理 应用的所有组件 的 状态,并以 相应的规则 保证 状态以一种 可预测的方式 发生变化. ...

  9. 理解vuex的状态管理模式架构

    理解vuex的状态管理模式架构 一: 什么是vuex?官方解释如下:vuex是一个专为vue.js应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证以一种可预测的 ...

随机推荐

  1. crontab任务调度

    基本语法 crontab [选项] 选项: -e:    编辑crontab定时任务 -l:    查询crontab任务 -r:    删除当前用户所有的crontab任务 2)参数说明 [root ...

  2. 【DNN】 安装问题

    http://blog.csdn.net/hwt0101/article/details/9153083 这是IIS 注册的问题  IIS 在安装VS 之前就装上了,所以 没有注册是上 F4 从新卸载 ...

  3. shell基础编程

    首先要注意的是,Ubuntu里的shell的sh和bash命令是有区别的 如下所示,Ubuntu下的sh指向的dash程序,而bash是dash的增强版,一些bash上能执行的程序在dash上不行 如 ...

  4. PHP————系统常量

    PHP常量默认为大小写敏感.传统上常量标识符总是大写的. PHP常量名和其它任何 PHP 标签遵循同样的命名规则.合法的常量名以字母或下划线开始,后面跟着任何字母,数字或下划线.用正则表达式是这样表达 ...

  5. Aspose WorkbookDesigner打开文件异常"Error xml namespace"

    错误描述: 平台是VS2010的.Net Framework 需要用Aspose的WorkbookDesigner打开excel文件的时候产生异常 异常码是Aspose.Cells.Exception ...

  6. vsftpd服务程序的三种认证模式

    vsftpd服务程序的三种认证模式的配置方法——匿名开放模式.本地用户模式以及虚拟用户模式.了解PAM可插拔认证模块的原理.作用以及实战配置方法,通过实战课程进一步继续学习SELinux服务的配置方法 ...

  7. 洛谷 P1045 麦森数 (快速幂+高精度+算位数骚操作)

    这道题太精彩了! 我一开始想直接一波暴力算,然后叫上去只有50分,50分超时 然后我改成万位制提高运算效率,还是只有50分 然后我丧心病狂开long long用10的10次方作为一位,也就是100亿进 ...

  8. solr启动时报错org.apache.solr.common.SolrException: undefined field text的解决办法

    solr启动时报错org.apache.solr.common.SolrException: undefined field text的解决办法 原创 2015年08月21日 20:47:40 标签: ...

  9. code vs 1245 最小的N个和

    1245 最小的N个和  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解       题目描述 Description 有两个长度为 N 的序列 A ...

  10. Qt之表单布局(QFormLayout)

    简述 QFormLayout管理输入型控件和关联的标签组成的那些Form表单. QFormLayout是一个方便的布局类,其中的控件以两列的形式被布局在表单中.左列包括标签,右列包含输入控件,例如:Q ...