Vue-6 Vuex(state, getters, mutations, actions, modules)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/FlowGuanEr/article/details/98502039

Vue-6 Vuex(state, getters, mutations, actions, modules)

在项目研发过程中,组件和组件之间的结构关系非常复杂,到处存在数据交互。 props$emit,路由传参是远远不足以支撑复杂的数据交互。

Vue 单独提供了 vuex 来做项目的状态管理,vuex 提供了公共数据的存储和操作方式,让组件之间的数据交互变得更简洁。

vuex 的核心理念就是存储一些供各个组件使用的数据,无论组件的嵌套结构如何,都能访问 vuex 中的数据。任何一个组件对其中的数据做了修改,在其他组件中的渲染会立即响应。

一、Vuex的安装和基本配置

vue-router 一样,要在项目中使用 vuex,需要先下载依赖包。

npm i vuex --save

安装成功之后在 src 文件夹中创建项目目录:

src
    └── App.vue
    └── main.js
    └── assets
    └── components
           // ...
    └── router
           // ...
    └── store // 状态管理库的目录
           index.js // 状态管理库的根配置文件
            

vuex 的基本配置:

// store>index.js
import Vue from 'vue'
import Vuex from 'vuex' // 引入 vuex

Vue.use(Vuex); 

// 创建一个状态管理库实例并且导出,以备使用
export default new Vuex.Store({
    // code
});

配置好了之后要将这个 Vuex.Store 实例对象传入根组件的实例化中,整个项目才能使用 vuex

// main.js
import Vue from 'vue'
import App from './App'

// 导入 store (省略)
import store from './vuex'

Vue.comfig.productionTip = false;

new Vue({
    el: '#app',
    // 将 store 传入根组件实例化的参数中
    store,
    components: { App },
    template: '<App/>'
});

vuexStore 类提供了五个配置,包含了可能出现的各种操作。

二、state

state 中存放的是 store 中所有的数据列表。

任何组件无论嵌套关系多复杂,都可以访问 state 中的数据。

// store>index.js 中的 state
state: {
   num: 100,
   bol: true
}

在任意组件中获取:

<!-- Any.vue -->
<template>
   <div class="any">
       <h2>num: {{ num }}</h2>
       <h2>bol: {{ bol }}</h2>
   </div>
</template>
<script>
   export default {
       name: 'Any',
       computed: {
          // 建议将对于 store 中的数据的获取或者操作写在计算属性中,这样当 store 中的数据变化时,也能及时响应
          num() {
              return this.$store.state.num;
          },
          bol() {
              return this.$store.state.bol;
          }
       }
   }
</script>

在其他任何组件中,对 state 中的数据做操作时,当前组件也会随之响应。

store 的好处之一就是无论组件和组件之间的嵌套结构是怎样的,都能共同访问 store 中的数据。

但是如果只是想访问 state 中的数据,且访问了很多个数据,那么为每一个数据单独的写一个计算属性,就显得很不理智。

vuex 提供 mapState 来批量的访问 state 中的数据

<!-- Any.vue -->
<script>
   import { mapState } from 'vuex';
   export default {
       computed: {
           ...mapState([
              'num',
              'bol'
           ]);
       }
   }
</script>

调用方式不变。

三、getters

给一个示例,state 中有一个数组,数组中有各种类型的数据,有两个组件(A和B),都只想要这个数组中的 number 列表。

state: {
   arr: [1, 4, 5, 'hello', true, 'world', 98]
}

我们可以在 A 和 B 的计算属性中先拿到 state 中的 arr,对其做处理,返回数字列表:

<!-- A.vue -->
<script>
   export default {
       name: 'A',
       computed: {
          numArr() {
              const arr = this.$store.state.arr;
              return arr.filter(function(item) {
                   if(typeof item == 'number') return item;
              });
          }
       }
   }
</script>

如果 B 组件的需求和 A 组件一样,那么 numArr 中的这段代码,在 B 组件中,又要重新写一遍。

这样就形成了代码冗余。

所以 vuex 提供了 getters,来根据代码需求过滤 state 中的数据,向外抛出过滤之后的数据。

// store>index.js 中导出的实例 
export default new Vux.Store({
    state: {
       arr: [1, 4, 5, 'hello', true, 'world', 98]
    },
    getters: {
        // getters 中的函数接收的第一个参数就是 state,可以通过它直接访问数据列表
        numArr: state => {
            const arr = state.arr;
            return arr.filter(function(item) {
                if(typeof item == 'number') return item;
            });
        }
    }
});

现在 A 、B 组件想要访问 state 中的 arr 中的数字列表,可以直接访问 getters

<!-- A.vue -->
<script>
    export default {
        name: 'A',
        computed: {
            // 求 state 中的 arr 中的所有数字的和
            numSum() {
                const arr = this.$store.getters.numArr;
                // 求和代码
            }
        }
    }
</script>
<!-- B.vue -->
<script>
    export default {
        name: 'B',
        computed: {
            // 求 state 中的 arr 中的所有数字的最大值和最小值
            numSum() {
                const arr = this.$store.getters.numArr;
                const min = Math.min(...arr);
                const max = Math.max(...arr);
            }
        }
    }
</script>

可以认为 gettersstore 的计算属性,当 state 中的数据变化的时候,getters 返回的数据也会随之变化,且只有当它的依赖值发生了变化之后才会变化。

如果需求再变化:A 组件希望拿到 arr 中所有的数字,B 组件希望拿到 arr 中所有的字符串。那可能需要向 getters 中传递想要的数据类型,实现按需获取数据的效果。

所以我们也可以以函数的形式访问 getters,并且向其传参。

// store 中的 getters
getters: {
    getArrByType(state) {
        return function(type) {
            const arr = state.arr;
            return arr.filter(function(item) {
                if(typeof item == type) return item;
            });
        }
    }
    /* 
    关于以函数的形式访问getters官网给出的是箭头函数简写形式(两种意义一样):
    getArrByType: (state) => (type) => {
        const arr = state.arr;
        return arr.filter(function(item) {
            if(typeof item == type) return item;
        });
    }
    */
}

访问:

// 任意组件的 computed
computed: {
    getData() {
        const numArr = this.$store.getters.getArrByType('number');
        const strArr = this.$store.getters.getArrByType('string');
    }
}

同样的,如果 getters 只是获取而不传参,也不用为其写很多个计算属性。

vuex 提供 mapGetters 来批量获取 getters

// 任意组件的 js
import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'getData1',
      'getData2',
      // ...
    ])
  }
}

调用时直接将这些数组中的参数作为变量名去使用即可。
如果觉得 store 中对于 getters 的命名太冗长,也可以在某个组件中为其单独命名

// 在当前组件中,gd 就代表 getArrByType
mapGetters({
    gd: 'getArrByType'
})

四、mutations

根据代码的需求,有时候我们可能也需要对 vuex 中的数据做一些更改。

vuex 规定,只能在其提供的 mutations 中修改 state 中的数据,否则严格模式下会直接报错。

// store>index.js 中 store 实例的导出
export default new Vuex.Store({
    state: {
        arr: [1,4,6]
    },
    mutations: {
        // 向 arr 中添加数据
        arrPush(state) {
            // mutations 中接收的函数的第一个参数也是 state
            state.arr.push(Math.floor(Math.random() * 100));
        }
    }
});

在任意组件中调用 mutations 中的函数需要使用到 storecommit

// 任意组件的 created
created() {
    // 调用 store 的 mutations 中的 arrPush
    this.$store.commit('arrPush');
}

也可以向 mutations 中的函数传参

mutations: {
    arrPush(state, data) {
        // data 就是在调用 mutations 时传递过来的参数
        state.arr.push(data);
    }
}

调用:

// 任意组件的 created
created() {
    this.$store.commit('arrPush', Math.random());
}

vuex 建议: 向 mutations中传递的参数尽量是一个对象,这样便于传输大量数据。

需要注意的是:mutations 中不能存在异步操作。

我们也可以使用 mapMutations 将组件中的 methods 映射成 mutations 中的函数。

methods: {
    ...mapMutations([
       // 在当前组件中,ap 就代表 arrPush  
       ap: 'arrPush'
    ])
  }

五、actions

vuex 不允许 mutations 中出现异步操作,那么有个异步操作刚好需要对 state 做改变,就需要写在 vuex 提供的 actions 中。

所以应该是在 actions 中写异步代码,在异步的某个过程中,如果需要对 state 做操作,就调用 mutations 中的函数。

// store>index.js 中的导出
export default new Vuex.Store({
    state: '500 miles',
    mutations: {
        setStr(state, newStr) {
            state.str = newStr;
        }
    },
    actions: {
        _setStr(context) {
            // Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象
            setTimeout(() => {
                context.commit('setStr', '500 miles away from home');
            });
        }
    }
});

调用:

// 任意组件的 created
created() {
    this.$store.dispatch('setStr');
}

actions 也可以接收 dispatch 时被传递的数据:

actions: {
    // 可以通过解构的方式从 context 中解构到 commit
    _setStr({commit}, newStr) {        
        setTimeout(() => {
            // newStr 就是调用 actions 时传递的参数
            context.commit('setStr', newStr);
        });
    }  
}

调用:

// 任意组件的 created
created() {
    this.$store.dispatch('setStr', '500 miles away from home');
}

六、modules

如果项目对于 state 的依赖很强,使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

将不同的需求的状态单独的声明成一个变量,然后将其导入到根对象的 modules 中,就能生效。

// store>index.js
import Vue from 'vue'
import Vuex from 'vuex' 

Vue.use(Vuex); 

const moduleA = {
    state: { 
       msg: 'data of a' 
    },
    mutations: { ... },
    actions: { ... },
    getters: { ... }
}

const moduleB = {
    state: { 
        msg: 'data of b' 
    },
    mutations: { ... },
    actions: { ... }
}

const store = new Vuex.Store({
    state: {
        msg: 'data of root'
    }
    modules: {
        a: moduleA,
        b: moduleB
    }
});

// 创建一个状态管理库实例并且导出,以备使用
export default store

对于不同模块的 state 中的数据的访问:

// 任意组件的 created
created() {
    const data1 = this.$store.state.msg;
    const data2 = this.$store.state.a.msg;
    const data3 = this.$store.state.b.msg;
}

除了state之外,getters、mutations、actions的访问方式不变。

如果要在子模块的 getters 中访问根 state 中的数据:

// 任意子模块的 getters
getters: {
    getData(state, getters, rootState) {
        // state: 当前模块的 state
        // rootState: 根模块的 state
    }
}

如果要在子模块的 actions 中访问:

// 子模块的actions
actions: {
    fn(context) {
        // context.state: 当前模块的 state
        // context.rootState: 根模块的 state
    }
}

猜你喜欢

转载自blog.csdn.net/FlowGuanEr/article/details/98502039