Vuex
本文于1048天之前发表,文中内容可能已经过时。
Vuex
关于Vuex
Vuex是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新,就像是一个针对浏览器的小型数据库一样。
核心部分
1.state:驱动应用的数据源
2.view:以声明方式将state映射到视图
3.actions:响应在view上的用户输入导致的状态变化
核心概念
Store
store基本上就是一个容器,它包含着你的应用中大部分的状态 (state)
store和普通的全局变量的区别是:
1.Vuex 的状态存储是响应式的。
2.不能直接改变 store 中的状态。改变 store 状态唯一的途径是显式地提交 (commit) mutation。
State
Vuex是单一状态树,拥有唯一的数据源。但这并不意味着它与模块化相冲突
获取Vuex中state状态的方法1
this.$store.state.a
或使用辅助函数mapState1
2
3
4
5
6
7
8
9// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: {
...mapState(['a','b','c']),
}
}
或1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 在单独构建的版本中辅助函数为 Vuex.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
}
})
}
Getter
getter可以认为是 store 的计算属性,返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//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)
},
// Getter 也可以接受其他 getter 作为第二个参数:
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
},
// 可以让 getter 返回一个函数,来实现给 getter 传参
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
})
获取Vuex中getter状态的方法1
2
3
4
5
6this.$store.getters.doneTodos
// -> [{ id: 1, text: '...', done: true }]
this.$store.getters.doneTodosCount
// -> 1
this.$store.getters.getTodoById(2)
// -> { id: 2, text: '...', done: false }
或使用辅助函数mapGetters1
2
3
4
5
6
7
8import { mapGetters } from 'vuex'
export default {
// ...
computed: {
...mapGetters(['doneTodosCount','anotherGetter'])
}
}
1 | //如果想将一个 getter 属性另取一个名字,使用对象形式 |
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,mutation 非常类似于事件1
2
3
4
5mutations: {
increment (state, n) {
state.count += n
}
}
要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:1
this.$store.commit('increment',10)
或者使用辅助函数mapMutations1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import { 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')`
})
}
}
使用常量替代 Mutation 事件类型
这是各种 Flux 实现中是很常见的模式,可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然1
2// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
1 | // store.js |
最后要强调一下–
Mutation必须是同步函数
因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。
Action
action和mutation的不同之处在于:
1.action提交的是mutation,而不是直接变更状态
2.action可以包含任意异步操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
举个需要异步的栗子1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16actions: {
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)
)
}
}
使用Action1
2//作对比 -> this.$store.commit('getTodo')
this.$store.dispatch('increment')
或者使用辅助函数mapActions1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import { 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')`
})
}
}
store.dispatch可以处理被触发的action的处理函数返回的Promise,并且调用store.dispatch后仍返回Promise:1
2
3
4
5
6
7
8
9
10actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
1 | store.dispatch('actionA').then(() => { |
1 | actions: { |
如果利用async/await,还可以:1
2
3
4
5
6
7
8
9
10
11// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
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
22const 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 的状态