vuex

什么是Vuex

“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)

但Vuex和单纯的全局变量有两点不同:

  1. Vuex的状态存储是响应式的,也就是说当store中的状态发生变化是,那么组件也会相应地得到更新。
  2. 不能直接改变store中的状态。唯一途径就是显式地提交(commit)mutation

State

在Vue组件中获得Vuex状态

由于Vuex的状态储存是响应式的,从store实例中读取最简单的方式就是计算属性中返回某个状态:

1
2
3
4
5
6
7
8
9
10
// 创建一个Counter组件
const Counter = {
template: `<div>{{ count }}</div>`,
compute:{
count(){
return store.state.count
// 在根实例中注册后可以用this.$store访问到 如 return this.$store.state.count
}
}
}

使用mapState辅助函数生成属性

1
2
3
4
5
6
7
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}

Getter

可以把Getter认为是store的计算属性,getter的返回值会根据它的依赖被缓存起来,只有当他的依赖发生变化才会重新计算。

通过属性访问

Getter接受state作为第一个参数,接受其他getter作为第二个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
getters: {
// ...
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}

store.getters.doneTodosCount // -> 1


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

注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。

通过方法访问

可以通过让getter返回一个函数,来实现给getter传参。

1
2
3
4
5
6
7
8
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}

store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。

mapGetter 辅助函数

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { mapGetters } from 'vuex'

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

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型(type)和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})

store.commit('increment')

提交载荷(Payload)

即向 store.commit 传入额外的参数,即mutation的载荷。

1
2
3
4
5
6
7
8
// ...
mutations: {
increment (state, n) {
state.count += n
}
}

store.commit('increment', 10)

载荷为对象:

1
2
3
4
5
6
7
8
9
10
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}

store.commit('increment', {
amount: 10
})

使用常量代替 Mutation 事件类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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
}
}
})

Mutation 必须时同步函数

因为在任何回调函数中进行的状态的改变都是不可追踪的。

在组件中提交Mutation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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')`
})
}
}

Action

Action 类似于 mutation,不同在于:

  • Action提交的是mutation,而不是直接更改状态。
  • Action可以包含任意异步操作。

注册Action

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})

使用ES6简化代码

1
2
3
4
5
actions: {
increment ({ commit }) {
commit('increment')
}
}

分发Action

Action 通过 store.dispatch 方法触发:

1
store.dispatch('increment')

购物车实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
)
}
}

Module

为了避免store对象变得臃肿,vuex允许我们将store划分为模块(module),每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 的状态