一、概念
Vuex
是一个专为 Vue.js
应用程序开发的 状态管理模式。
因为模块间是不共享作用域的,平时用来解决共享参数的方法就是通过组件传参,一旦项目变得很庞大,管理和维护这些值将是相当棘手的事情。
这是我们就可以使用Vuex
集中式存储管理所有组件的状态。
二、整体运作流程
三、使用场景
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
四、为啥不使用其他的状态管理器,如redux
因为 Vuex
是一个专门为 Vue
应用所设计。这使得它能够更好地和Vue
进行整合,同时提供简洁的 API 和改善过的开发体验。
五、五大核心
State
、Getters
、Mutations
、Actions
、Modules
mapState
、mapGetters
、mapMutations
、mapActions
可以把 Vuex
理解为 “前端的数据库”。
state
就是数据库。
getters
是用来从数据库取数据的。既然是取,那么肯定是不能修改的,所以, getters
是一个“纯函数”,即不会对元数据造成影响的函数。
mutations
是用来修改 state
的。只能是同步操作。
actions
可以理解成:我们从后端拿到数据,总得做个处理。处理完了再存到数据库中,当然也可以不处理,就是直接mutation。包含异步操作。
State
https://vuex.vuejs.org/zh/guide/state.html
// 创建一个 Counter 组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入,并且在测试组件时需要模拟状态。
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中
(需调用 Vue.use(Vuex)):
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,
且子组件能通过 this.$store 访问到。让我们更新下 Counter 的实现:
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
mapState
: 当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性。
// 在单独构建的版本中辅助函数为 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
}
})
}
mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
Getter
当组件多处要使用某属性时,我们可以使用getters
(可以理解成store的计算属性)得到。getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
// 通过属性访问
store.getters.doneTodos // -> [{ id: 1, text: '...' }]
getters: {
// ...第一个参数:state, 第二个参数:其他getters
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。
// 通过方法访问
注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
doneCount: 'doneTodosCount', // 起别名
'anotherGetter',
// ...
])
}
}
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法
// 俩种提交方法
store.commit('increment', {amount: 10})
store.commit({
type: 'increment',
amount: 10
})
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Action
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
// context 上下文对象,context.state, context.rootState
context.commit('increment')
}
}
})
// 分发Action, 调用Action
store.dispatch('increment')
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
// 在组件中分发 Action
this.$store.dispatch('xxx')
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
组合Action
- 处理异步操作,返回Promise
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch('actionA').then(() => {
// ...
})
- 利用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())
}
}
一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
Module
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
state
回事模块的局部状态对象。
对于模块内部的 action
,局部状态通过 context.state
暴露出来,根节点状态则为 context.rootState
。
getter
、action
都可以拿到第三个参数rootState
。
命名空间:namespaced: true