State
Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性
import { mapState } from 'vuex'
computed:{
//第一种写法
...mapState({
count: state => state.count
})
//第二种写法(city为store的文件名)
...mapState('city', ['location']),
//第三种写法
...mapState(['location']),
}
Getter
计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 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)
}
}
})
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getter 也可以接受其他 getter 作为第二个参数
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
第一种写法
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
第二种写法
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
第三种写法(你想将一个 getter 属性另取一个名字,使用对象形式)
...mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
Mutation
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
mutation 必须是同步函数
- 最好提前在你的 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该
- 使用
Vue.set(obj, 'newProp', 123)
, 或者 - 以新对象替换老对象。
-
state.obj = { ...state.obj, newProp: 123 }
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
store.commit('increment')
提交载荷(Payload)
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
对象风格的提交方式
store.commit({
type: 'increment',
amount: 10
})
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
使用常量替代 Mutation 事件类型
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
//第一种简写写法
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
//第二种写法
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
//第三种写法
const $store = $vm.$store || $vm.store
$store.commit('increment')
}
}
Action
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。
需要调用 commit
很多次的时候
actions: {
increment ({ commit }) {
commit('increment')
}
}
分发 Action
Action 通过 store.dispatch
方法触发
store.dispatch('increment')
mutation 必须同步执行,Action 就不受约束!我们可以在 action 内部执行异步操作
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Actions 支持同样的载荷方式和对象方式进行分发
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
看一个更加实际的购物车示例,涉及到调用异步 API 和分发多重 mutation
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)
)
}
}
组件中分发 Action
注意: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` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
// 第二种方式
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
组合 Action
第一种方式
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
第二种方式
store.dispatch('actionA').then(() => {
// ...
})
在另外一个 action 中也可以
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
如果我们利用 async / await
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
Module
项目结构
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
表单的实例演示
<input :value="message" @input="updateMessage">
computed: {
...mapState({
message: state => state.obj.message
})
},
methods: {
updateMessage (e) {
this.$store.commit('updateMessage', e.target.value)
}
}
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
双向绑定的计算属性
<input v-model="message">
// ...
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}