vuex应用的核心就是store(仓库),仓库中包含着应用中大部分的状态(state)。
1. vuex的状态是响应式的,当vue组件从store中读取状态时,若store的状态发生变化,那么相应的组件也会相应地得到高效的更新。
2. 不能直接改变store中的状态。改变store中的状态唯一途径就是显式地提交(commit) mutations。
创建state实例
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
现在,通过store.state就可以获取 状态对象,以及通过store.commit方法触发状态变更:
store.commit('increment')
console.log(store.state.count) => 1
我们是通过提交mutation的方式,而非直接改变store.state.count。这样可以更明确地追踪到状态的变化,也让我们更有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。实现如时间穿梭般的体验。
一.state
vuex使用单一状态树,用一个对象就包括了全部的应用层级状态,每个应用将紧紧包含一个stroe实例。单一状态树让我们能够直接定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用的状态快照。(时间穿梭于始终。)单状态数跟模块化并不冲突,能实现将状态和状态变更事件分布于各个子模块中。
vue通过stroe选项,将状态从根组件【注入】到每一个子组件中
const app = new Vue({
el:'#app',
store,
compents:{counter},
template:`
<div class = 'app'>
<counter></counter>
<div>
`
})
以上代码在根实例中注册了store选项,该store实例会注入到根组件下的所有子组件中,且子组件能通过this.$store访问到。
const counter = {
template:`<div>{{count}}</div>`,
computed:{
count(){
return this.$store.state.count
}
}
}
如上,子组件counter中的计算属性获取了store实例
当需要获取多个状态时候,不必声明多个计算属性。
可以使用mapState辅助函数来帮助我们生成计算属性
import {mapState} from 'vuex'
export default {
computed:mapState({
count: state => state.count,
countAlias: 'count',
countPlusLocalState(state){
return state.count + this.localCount
}
})
}
使用vuex并不意味着需要将所有的状态放入vuex。如果有些状态严格属于某个单组件,最好还是作为组件的局部状态。
二.Getters
有时候需要从store中的state中派生出一些状态,例如对列表进行过滤并计数:
computed:{
doneTodosCount(){
return this.$store.state.todos.filter(todo => todo.done).length
}
}
如果在多个子组件中都需要重复用到这个计算属性,那么就必须在每个子组件中都复制粘贴这个计算属性。
在vuex在store实例中定义了getters(可以认为是store的计算属性),那么子组件想要计算如上情况时,就只需要寻找在挂载在根实例上的store中的getters就可以了。
(store中的getters类似于vue实例的computed)
如下vuex实例:
const store = new Vuex({
state:{
todos:{
{id: 1,text: '...',done: true},
{id: 2,text: '...',done: false}
}
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
可以看到,getters中进行了有关state的计算。因此所有的子组件都可以直接从根组件的store.getters得到“计算属性”
在组件中的使用如下:
computed:{
doneTodosCount () {
//通过获取从根组件上传来的stroe的对象的getters属性
return this.$store.getters.doneTodesCount
}
}
三.mutations
更改store中的状态的唯一方法是提交 mutation。mutations类似于事件,每一个mutation都有一个事件类型(type)和一个回调函数(handler)。这个回调函数就是我们实际进行状态更改的地方。
要唤醒一个mutation handler,使用相应的type调用store.commit方法:
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { //变更状态 state.count++ } } }) store.commit('increment') //increment调用commit
可以向commit中传入额外的参数,即mutation的载荷(payload),一般是一个对象,可以包含多个字段并且易读。
//··· mutations: { increment: (state, payload){ state.count += payload.amount } } store.commit('increment',{ amount: 10 }) //另一种风格是直接使用包含type属性的对象: store.commit({ type: 'increment', amount: 10 })
mutation必须是同步函数。
任何在回调函数中进行的状态的改变都是不可追踪的在组件中提交mutations时,可以使用
this.$store.commit(xxx)
提交mutation,或者使用mapMutations辅助函数将组件中 的methods映射为store.commit
调用import { mapMuations } from 'vuex' export default { //··· methods: { ...mapMutations([ 'increment' //映射 this.increment() 为this.$store.commit('increment') ]), ...mapMutations([ add: 'increment' //映射this.add()为this.$store.commit('increment') ]) } }
四.Actions
Actions类似于mutations,不同在于
- Acitons提交的mutations,而不是直接变更状态
- Action可以包含任意异步操作(mutations必须是同步的)
const store = new Vuex.Store({
state:{
count: 0
},
mutaitons: {
increment (state) {
state.count ++
}
},
actions: {
increment (context){
context.commit('increment')
}
}
})
Actions函数接受一个与store实例具有相同方法和属性的context对象。一般通过es6中的 参数解构来优化代码
actions:{
increment ({ commit }) {
commit( 'increment' )
}
}
分发 Action
Action通过store.dispatch方法触发:store.dispatch('increment')
之所以不直接commit mutation是因为mutation 必须是同步执行的。
于是使用dispatch分发actions,就可以在action中进行异步操作了Action的分发方式跟mutation的commit方式一样
//载荷方式 store.dispatch('increment',{ amount: 10 }) //对象方式 store.dispatch({ type: 'incrementAsync', amount: 10 })
actions实例,调用了异步api和分发多重mutations。通过提交mutations来记录action产生的副作用(即状态变更):
actions: { checkout ({commit, state},products) { //把当前购物车的物品备份起来 const savedCartItems = [...state.cart.added] //发出结账请求,然后乐观地清空购物车 commit(types.checkout_request) //购物api接受一个成功回调函数和一个失败回调 shop.buyProducts( products, //成功操作 () => commit(types.checkout_success), //失败操作 () => commit(types.checkout_failure,savedCartItems) ) } }
在组件中分发patch
你在组件中使用 this.$store.dispatch(‘xxx’) 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):import { mapActions } from 'vuex' export default { // ... methods: { ...mapActions([ 'increment' // 映射 this.increment() 为 this.$store.dispatch('increment') ]), ...mapActions({ add: 'increment' // 映射 this.add() 为 this.$store.dispatch('increment') }) } }
组合actions
action通常是异步的,要想处理更加复杂的异步流程,需要组合多个action。可以让store.dispatch处理被触发的action回调函数返回promise,并且dispatch仍旧返回promise。actions: { actionA ({ commit }) { return new Promise( (resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() } },1000) }) } //dispatch返回的是promise
现在就可以处理actionA返回来的promise
store.dispatch('actionA').then(() => { //... })
在另一个action中也可以
actions: { //... actionB( {dispatch, commit}) { return dispatch('actionA').then( () => { commit('someOtherMutation') }) } } //以此来实现组合多个actions
一个store.dispatch在不同模块中可以触发多个action函数。这种情况下,只有当所有的触发函数完成后,返回的promise才会执行