Vue中的vuex介绍与使用(通俗版)

Vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,单一状态树,通俗理解就是一个应用的数据集合,可以想象为一个“前端数据库”(数据仓库),让其在各个页面上实现数据的共享包括状态,并且可操作。详情官方解释

Vuex的组成

Vuex分成五个部分:

  1. State:单一状态树
  2. Getters:状态获取
  3. Mutations:触发同步事件
  4. Actions:提交mutation,可以包含异步操作
  5. Module:将vuex进行分模块

前四个特征就是核心。

正如官网vuex图示,Vuex的数据总是“单向流动”

  • 用户访问页面并触发action
  • action提交mutation事件
  • mutation事件更改state状态
  • state状态改变后更新页面(vue comptents)

Vuex 规定,属于应用层级的状态只能通过 Mutation 中的方法来修改,而派发 Mutation 中的事件只能通过 action。

用法

从左到右,从组件出发,组件中调用 action,在 action 这一层级我们可以和后台数据交互,比如获取初始化的数据源,或者中间数据的过滤等。然后在 action 中去派发 Mutation。Mutation 去触发状态的改变,状态的改变,将触发视图的更新。

注意事项

  • 数据流都是单向的
  • 组件能够调用 action
  • action 用来派发 Mutation
  • 只有 mutation 可以改变状态
  • store 是响应式的,无论 state 什么时候更新,组件都将同步更新

方式

方式一:可以将state,getter,mutation,action写到一个store.js文件中可以参考
方式二:在src目录下我们创一个vuex文件夹,分别创建index.js,mutations.js,state.js,getters.js,actions.js可以参考

这里以方式一进行实例

State
在 store 中的 state 对象,可以理解为 Vue 实例中的 data 对象,它用来保存最基本的数据。

import Vue from 'Vue';
import Vuex from 'Vuex';
Vue.use(Vuex);
let store = new Vuex.Store({
    state: {
        count: 0,
    }
});

如果希望Vuex状态更新,相应的Vue组件也得到更新,最简单的方法就是在Vue的computed(计算属性)获取state

// Counter 组件
const Counter = {
    template: `<div>{{ count }}</div>`,
    computed: {
        count () {
            return store.state.count;
        }
    }
}

上面的例子是直接操作全局状态store.state.count,那么每个使用该Vuex的组件都要引入。为了解决这个,Vuex通过store选项,提供了一种机制将状态从根组件注入到每一个子组件中。

// 根组件
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);
const app = new Vue({
    el: '#app',
    store,
    components: {
        Counter
    },
    template: `
        <div class="app">
            <counter></counter>
        </div>
    `
})

通过这种注入机制,就能在子组件Counter通过this.$store访问:

// Counter 组件
const Counter = {
    template: `<div>{{ count }}</div>`,
    computed: {
        count () {
            return this.$store.state.count
        }
    }
}

mapState函数
computed: {
    count () {
        return this.$store.state.count
    }
}

这样通过count计算属性获取同名state.count属性,是不是显得太重复了,我们可以使用mapState函数简化这个过程。

import { mapState } from 'vuex';

export default {
    computed: mapState ({
        count: state => state.count,
        countAlias: 'count',    // 别名 `count` 等价于 state => state.count
    })
}

还有更简单的使用方法:

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

Getters
当需要对 store 中的数据进行处理,或者需要对处理后的数据在多个组件进行复用,就可以使用 Getters 来处理,Getters 也可以理解为 Vue 中的计算属性 (computed)。如下:

computed: {
    doneTodosCount () {
        return this.$store.state.todos.filter(todo => todo.done).length
    }
}

如果多个组件都要进行这样的处理,那么就要在多个组件中复制该函数。这样是很没有效率的事情,当这个处理过程更改了,还有在多个组件中进行同样的更改,这就更加不易于维护。
Vuex中getters对象,可以方便我们在store中做集中的处理。Getters接受state作为第一个参数:

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

在Vue中通过store.getters对象调用。

computed: {
  doneTodos () {
    return this.$store.getters.doneTodos
  }
}

Getter也可以接受其他getters作为第二个参数:
getters: {
  doneTodos: state => {
      return state.todos.filter(todo => todo.done)
  },
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

mapGetters辅助函数
与mapState类似,都能达到简化代码的效果。mapGetters辅助函数仅仅是将store中的getters映射到局部计算属性:

import { mapGetters } from 'vuex'

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

上面也可以写作:

computed: mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])

所以在Vue的computed计算属性中会存在两种辅助函数:

import { mapState, mapGetters } from 'vuex';

export default {
    // ...
    computed: {
        mapState({ ... }),
        mapGetter({ ... })
    }
}

Mutations
之前也说过了,更改Vuex的store中的状态的唯一方法就是mutations。
每一个mutation都有一个事件类型type和一个回调函数handler。

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

调用mutation,需要通过store.commit方法调用mutation type:

store.commit('increment')

Payload 提交载荷
也可以向store.commit传入第二参数,也就是mutation的payload:

mutaion: {
    increment (state, n) {
        state.count += n;
    }
}

store.commit('increment', 10);

单单传入一个n,可能并不能满足我们的业务需要,这时候我们可以选择传入一个payload对象:

mutation: {
    increment (state, payload) {
        state.totalPrice += payload.price + payload.count;
    }
}

store.commit({
    type: 'increment',
    price: 10,
    count: 8
})

mapMutations函数
不例外,mutations也有映射函数mapMutations,帮助我们简化代码,使用mapMutations辅助函数将组件中的methods映射为store.commit调用。

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment' // 映射 this.increment() 为 this.$store.commit('increment')
    ]),
    ...mapMutations({
      add: 'increment' // 映射 this.add() 为 this.$store.commit('increment')
    })
  }
}

注 Mutations必须是同步函数。
如果我们需要异步操作,Mutations就不能满足我们需求了,这时候我们就需要Actions了。

Actions
与 mutations 类似,不同模块的 actions 均可以通过 store.dispatch 直接触发。

const moduleA = {
    state: {
        count: 1
    },
    mutations: {
        sayCountA(state) {
            console.log('Module A count: ', state.count);
        }
    },
    actions: {
        maAction(context) {
            context.dispatch('mbAction');
        }
    }
};
const moduleB = {
    state: {
        count: 2
    },
    mutations: {
        sayCountB(state, num) {
            console.log('Module B count: ', state.count+num);
        }
    },
    action: {
        mbAction({ commit, rootState }) {
            commit('sayCountA');
            commit('sayCountB', rootState.a.count);
        }
    }
};
const store = {
    modules: {
        a: moduleA,
        b: moduleB
    }
};
store.dispatch('maAction'); // Module A count: 1、Module B count: 3

action 的回调函数接收一个 context 上下文参数,context 包含:1. state、2. rootState、3. getters、4. mutations、5. actions 五个属性,

Module
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

场景

个人认为vuex适合运用于一个视图内组件与组件之间的组合传递,不适用于跨页面,但是可以共用,为了不因用户刷新页面,要进行初始化再次调用。在自己写一些Ui组件给大家或者开源用的话,不适用于写在vuex中,应该暴露所接收的Props,通过$emit来进行触发事件,一些关键性全局状态,不也适合存vuex中,你可以选择localStorage,sessionStorage。

发布了82 篇原创文章 · 获赞 46 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/weixin_43720095/article/details/89764560