一.什么是vuex
官方说法:Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
个人理解:Vuex提供了一个储存数据的地方,但它的核心作用在于封装操作数据的方法。
二.为什么要用Vuex?
我们知道组件之间是独立的,组件之间想要实现通信,除了vuex还有三种方法,父子组件之间通信使用$emit和props,非父子组件只能用eventbus(事件总线),但事件总线使用起来较为繁琐。试想一下,当做中大型项目时,面对一大堆组件之间的通信,还有一大堆的逻辑代码,会不会很抓狂??那为何不把组件之间共享的数据给“拎”出来,在一定的规则下管理这些数据呢? 这就是Vuex的基本思想了。
三.怎么使用Vuex?
从官方文档可以看到,vuex的核心就是store对象(数据库管理系统),store有五个核心概念,分别是state,getter,mutation,action,module。我们先来概述一下,然后再结合代码来具体讲解。
store对象中有四部分,state(相当于数据存储器),就是存储数据的地方。getter(数据处理方法),很多时候我们需要把数据处理之后再用,每次用的时候都处理很明显不方便,所以我们把数据处理方法集中放到getter中。mutation(同步改变数据方法),当我们要改变state中的数据时,不能直接改变state中的数据,只能通过mutation中的方法来改变,mutation中的方法必须是同步的。action(异步改变数据方法)。当我们要异步改变state中的数据时,就要通过action,但action改变数据也是依靠mutation。
借助官方的图可以更好地理解:
在组件中改变vuex中的数据时,首先使用dispatch(就是store自带的调用actions的方法)调用actions中的异步改变数据方法,然后由actions中的方法使用commit(store调用mutation的方法)调用mutations中的方法改变数据。当然,图中这是异步改变数据,当我们只是同步改变数据时,直接使用commit调用mutations中的方法就可以。通过mutations改变的state中的数据会响应式的显示在页面上。
module是一个store模块化的方案,当我们在store中的数据很多时,各种方法写在一起看起来会很臃肿,为了解决这个问题,Vuex 允许我们将 store 分割成模块。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
结合代码来实战演练一下:
1.创建store实例
Vue.use(Vuex) const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })在上面的代码中,首先new了一个vuex实例,实例中有state和mutations。state对象就是我们的数据库, 其中保存了一个属性count值为0。mutations对象中保存的是我们操作state的方法。
然后我们将store挂载在vue实例上, store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。
new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })这时我们就可以在vue实例中使用vuex了,我们要把需要用到的store中的值写在计算属性中。
<template> <div id="app"> {{count}} </div> </template> <script> export default { name: 'App' }, computed: { count () { return this.$store.state.count } } </script>但当数据很多的时候,都这样声明在计算属性中就显得很麻烦,vuex提供了mapState 辅助函数,我们可以像下面这样改写一下
import {mapState} from 'vuex' export default { name: 'HomeHeader', computed: { mapState(['count']) } }如果是多个数据的话我们还可以用对象展开运算符
computed: { ...mapState(['count','number']) }这种写法对于某些童鞋来说可能会比较懵,我们来分析一下,
mapState是一个函数,返回一个对象,他的本质大概是这个样子滴:
function mapState(){return {count:1,number:2}} var data={ mapState() } //这样肯定会报错的 var data = { ...mapState() } //这样就能正确展开 data = { count:1; number:2; }2.mutation
想要改变count时,必须使用commit调用mutations中的方法
this.$store.commit('increment') //调用mountations中的方法 console.log(store.state.count) // -> 1同样的mutations中也提供了辅助函数mapMutations:
...mapMutations(['increment']) this.increment()方法中是可以传入参数的,mutatinos方法中默认传入的第一个参数为state,actions和getter的方法也是一样的:
mutations: { increment (state, n) { state.count += n } } this.$store.commit('increment', 10) //或者这样 ...mapMutations(['increment']) this.increment(10)3.action
Action 提交的是 mutation,而不是直接变更状态。action不同于mutation,action 函数接受一个与 store 实例具有相同方法和属性的 context 对象而不是store本身。我们使用参数结构省略了context,写成了{commit}
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } })调用actions中的方法使用dispatch
this.$store.dispatch('incrementAsync')同样的actions也有辅助函数mapActions,在这里不再多说。
store.dispatch()方法返回的是一个promise对象,所以我们可以像使用promise时那样写
store.dispatch('actionA').then(() => { // ... })4.getter
很多时候我们直接从store中拿到的数据是不能使用的,需要经过处理,但每次使用的时候都写代码处理一次就比较麻烦,所以vuex为我们提供了getter,在getter中保存对数据处理的方法
Vue.use(Vuex) const store = new Vuex.Store({ state: { todos: [1,2,3] }, mutations: { increment (state) { state.count++ } } }) computed: { doneTodosCount () { return this.$store.state.todos.filter((n) => n<2).length } }对于上面的这种情况,我们可以将下面这段代码加到store实例中
getters: { doneTodos: state => { return state.todos.filter((n) => n<2) } }然后通过下面这样来调用
this.$store.getters.doneTodos