本文内容主要参考官方教程, 为了方便理解, 用更加通俗的文字讲解 vuex, 也原文内容做一些重点引用希望会对你有所帮助
学习 vue 之前, 最重要是弄懂两个概念, 一是 what, 要理解 vuex 是什么; 二是 why, 要清楚为什么要用 vuex
Vuex 是什么?
Vuex 类似 React 里面的 Redux 的状态管理器, 用来管理 Vue 的所有组件状态
为什么使用 Vuex?
当你打算开发大型单页应用(SPA), 会出现多个视图组件依赖同一个状态, 来自不同视图的行为需要变更同一个状态
遇到以上情况时候, 你就应该考虑使用 Vuex 了, 它能把组件的共享状态抽取出来, 当做一个全局单例模式进行管理这样不管你在何处改变状态, 都会通知使用该状态的组件做出相应修改
下面讲解如何使用 Vuex
一个简单的 Vuex 示例
本文就讲解安装 Vuex, 直接通过代码讲解 Vuex 使用
- import Vue from 'vue';
- import Vuex from 'vuex';
- Vue.use(Vuex);
- const store = new Vuex.Store({
- state: {
- count: 0
- },
- mutations: {
- increment(state) {
- state.count++
- }
- }
- })
上面就是一个简单的 Vuex 示例, 每一个 Vuex 应用就是一个 store, 在 store 中包含组件中的共享状态 state 和改变状态的方法(暂且称作方法)mutations
需要注意的是只能通过 mutations 改变 store 的 state 的状态, 不能通过 store.state.count = 5; 直接更改(其实可以更改, 不建议这么做, 不通过 mutations 改变 state, 状态不会被同步)
使用 store.commit 方法触发 mutations 改变 state:
- store.commit('increment');
- console.log(store.state.count) // 1
一个简简单单的 Vuex 应用就实现了
在 Vue 组件使用 Vuex
如果希望 Vuex 状态更新的时候, 组件数据得到相应的更新, 那么可以用计算属性 computed 获取 state 的更新状态
- const Counter = {
- template: `<div>{{ count }}</div>`,
- computed: {
- count () {
- return store.state.count;
- }
- }
- }
每一个 store.state 都是全局状态, 在使用 Vuex 时候需要在根组件或 (入口文件) 注入
- // 根组件
- 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([
- 'count'
- // 映射 this.count 为 store.state.count
- ])
Getters 对象
如果我们需要对 state 对象进行做处理计算, 如下:
- 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: {
- ...mapGetters([ ... ]),
- ...mapState([ ... ])
- }
- }
- Mutations
之前也说过了, 更改 Vuex 的 store 中的状态的唯一方法就是 mutations
每一个 mutation 都有一个事件类型 type 和一个回调函数 handler
调用 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,Mutations 就不能满足我们需求了, 这时候我们就需要 Actions 了
Actions
Action 类似于 mutation, 不同在于:
Action 提交的是 mutation, 而不是直接变更状态
Action 可以包含任意异步操作
让我们来注册一个简单的 action:
- var store = new Vuex.Store({
- state: {
- count: 0
- },
- mutations: {
- increment: function(state) {
- state.count++;
- }
- },
- actions: {
- increment: function(store) {
- store.commit('increment');
- }
- }
- });
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象, 因此你可以调用 context.commit 提交一个 mutation, 或者通过 context.state 和 context.getters 来获取 state 和 getters
分发 Action
Action 通过 store.dispatch 方法触发:
乍一眼看上去感觉多此一举, 我们直接分发 mutation 岂不更方便? 实际上并非如此, 还记得 mutation 必须同步执行这个限制么? Action 就不受约束! 我们可以在 action 内部执行异步操作:
- actions: {
- incrementAsync ({ commit }) {
- setTimeout(() => {
- commit('increment')
- }, 1000)
- }
- }
Actions 支持同样的载荷方式和对象方式进行分发:
- // 以载荷形式分发
- store.dispatch('incrementAsync', {
- amount: 10
- })
- // 以对象形式分发
- store.dispatch({
- type: 'incrementAsync',
- amount: 10
- })
同样地, action 也有相对应的 mapActions 辅助函数
mapActions
mapActions 辅助函数跟 mapMutations 一样都是组件的 methods 调用:
- import { mapActions } from 'vuex'
- export default {
- // ...
- methods: {
- ...mapActions([
- 'increment' // 映射 this.increment() 为 this.$store.dispatch('increment')
- ]),
- ...mapActions({
- add: 'increment' // 映射 this.add() 为 this.$store.dispatch('increment')
- })
- }
- }
- mutation-types
关于 mutation-types 方面的讲解官方文档很少说明, 但在实际的中大项目中, 对 ==mutation-types== 的配置是必不可少的, Vuex 的文档只讲解了 state,getters,mutation,actions 四个核心概念, 下面我简单补充下 mutation-types 的使用
顾名思义,==mutation-types== 其实就是 mutation 实例中各个方法的设定, 一般要 mutation 方法前先在 mutation-types 用大写写法设定, 再在 mutation 里引入使用, 下面看看项目实际使用:
项目组织结构
image
在 mutation-types 定义好 mutation 的方法结构:
- //SET_SINGER,SET_SONG 为 mutation 中要使用的方法名
- export const SET_SINGER = 'SET_SINGER'
- export const SET_SONG = 'SET_SONG'
在 mutation 中导入使用:
- import * as types from ',/mutation-types.js'
- const mutations = {
- [types.SET_SINGER](state, singer) {
- ....
- },
- [types.SET_SONG](state, song) {
- ....
- }
- }
结语
看完上面对 vuex 的讲解相信你已经入门了, 现在可以看看具体的项目加深理解, 可以参考我的 github 一个购物车例子: https://github.com/osjj/vue-shopCart
image
image
来源: http://www.jianshu.com/p/6a59a3a7814e