初步学习Vuex

什么是vuex

vuex是专门为vue.js开发的状态管理模式,它将全部组件的状态集中的存储在一块儿,而且用相应的规则使这些状态以一种可预测的方式发生改变。vue

vuex是作什么用的

前面说到vuex是专门为vue.js开发的状态管理模式,那么究竟什么是“状态管理模式”呢? 以我目前对状态的理解就是组件在某个时刻呈现出的视图、数据的状态。在vue的项目开发中咱们常常会遇到多个组件共享某个状态。即:多个视图依赖同一组件 或者 不一样视图的行为须要改变同一状态。
对于这种状况父子组件以前经过props进行传参是很繁琐的,同时兄弟组件是没办法作到的。而且不一样视图之间只能经过事件触发或直接引用的方式传递数据。这显然是很麻烦的。
所以就有了vuex,他将组件的全部状态抽离出来,存储在全局的一个仓库里,这样无论组件在哪一个位置均可以获取到状态或者触发行为。vuex就像一个巨大的仓库同样能够为咱们存储着全部组件的状态和改变状态的行为,同时维持着视图和状态之间的独立性,使得代码变得结构更加清晰且易维护。vuex

vuex要如何使用

  • 首先安装vuex

npm install vuex --savenpm

  • 引入vuex

import Vuex from 'vuex'
vue.use(Vuex)数组

  • 建立一个 store,仅须要提供一个初始 state 对象和一些 mutation:
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
复制代码
  • 获取状态对象
// 建立一个 Counter 组件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}
复制代码

因为 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态promise

  • 经过 store.commit 方法触发状态变动:
store.commit('increment')
复制代码

注意:改变状态的惟一方式就是经过commit提交mutationbash

  • mapState 辅助函数
    当组件获取多个状态时,为了不因将状态声明为计算属性而是代码重复冗余,咱们可使用mapState辅助函数
//引入mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭头函数可以使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了可以使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}
复制代码

当映射的计算属性的名称与 state 的子节点名称相同时,咱们也能够给 mapState 传一个字符串数组。app

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])
复制代码
  • vuex中的Getter
    Getter至关于vuex中的计算属性,是对一些状态的操做(若是一些状态的操做会被屡次使用不妨将他们写入Getter中调用),当他所依赖的状态发生改变的时候,Getter返回的值也会发生相应的变化。
//Getter 接受 state 做为其第一个参数,接受其余 getter 做为第二个参数:
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)
    }
  }
})
复制代码

经过属性访问Getter异步

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
复制代码
  • mapGetters 辅助函数
    mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}
复制代码
  • Vuex 中的 mutation
    vuex中的mutation相似于事件,每一个mutation都由表明此事件的名称(type)和回调函数两部分组成,回调函数中就是进行状态更改的地方,在大型项目中为了使整个 app 包含的 mutation 一目了然,一般会使用常量替代 Mutation 事件类型,将这些常量单独写在一个.js文件中。 Mutation 必须是同步函数,若要进行异步操做能够将其写在action中
//它会接受 state 做为第一个参数:
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变动状态
      state.count++
    }
  }
})
复制代码
  • 提交载荷(Payload)
    你能够向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)大多数时候载荷是一个对象,这样能够包含多个字段而且记录的 mutation 会更易读:

两种提交方式async

//方式一
store.commit('increment', {
  amount: 10
})
*******
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
//方式二  ——对象风格的提交方式
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
*******
store.commit({
  type: 'increment',
  amount: 10
})
复制代码
  • vuex中的Action
    Action 相似于 mutation,不一样在于:
  1. Action 提交的是 mutation,而不是直接变动状态。
  2. Action 能够包含任意异步操做。

注册一个简单的action函数

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  //action接受一个和store有相同属性和方法的context 对象
  //所以你能够调用 context.commit 提交一个 mutation
  //经过 context.state 和 context.getters 来获取 state 和 getters
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})
复制代码
  • 触发Action
store.dispatch('increment')

// 以载荷形式分发
store.dispatch('incrementAsync', {
  amount: 10
})

// 以对象形式分发
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})
复制代码
  • 在组件中分发 Action
import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

      // `mapActions` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }
}
复制代码
  • 组合 Action
  • store.dispatch能够处理action返回的promise,而且 store.dispatch 仍旧返回 Promise:
store.dispatch('actionA').then(() => {
  // 处理Action返回的promise
})
复制代码
  • 使用async / await时,组合Action
// 假设 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}
复制代码
  • vuex中的module
    若是将每一个组建的状态,都放在同一个store中,那么整个store会变得很臃肿,因此vuex提供了module这个属性,使得每一个模块拥有本身的 state、mutation、action、getter、甚至是嵌套子模块
const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

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

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

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
复制代码
  • 命名空间
    若是但愿你的模块具备更高的封装度和复用性,你能够经过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的全部 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模块内容(module assets)
      state: { ... }, // 模块内的状态已是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})
复制代码