前言
Vuex是什么
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension (opens new window),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
这是官网对Vuex的定义,我们可以看出:
- Vuex 是专门为 Vue.js 设计的状态管理库
- 采用集中管理应用中多个组件之间需要共享的数据,解决了复杂组件通信,实现数据共享
- 集中管理的数据以一种可预测的方式变化
Vuex应用场景
组件化是Vue.js核心之一,组件化则可以给我们带来更快的开发效率和更好的维护性,在Vue项目中,每个页面都可以看成是一个组件,大多数场景下的组件都并不是独立存在的,而是相互协作共同构成了一个复杂的业务功能,这就导致了组件状态间错综复杂的关系,管理好这些状态也变的尤为重要。通过官网的定义,我们知道Vuex设计的初衷就是解决这个问题的,那是不是所有Vue.js的应用都可以使用Vuex呢?
Vuex 可以帮助管理共享状态,但也附带了更多的概念和框架,如果一个应用足够简单,只有几个简单的页面,使用 Vuex 无疑是繁琐冗余的,会无形中增加开发工作量,从而影响开发效率。但是当我们构建一个中大型单页应用,多个组件依赖同一状态,多个组件需要通过内部的方法去更改同一状态时,Vuex便是最好的选择。
Vuex核心概念
从官网的这张图可以看出,Vuex的核心是state、actions、mutations。
state
state是应用组件共享的状态,存储在 Vuex 的核心仓库store中,形成单一状态树,store内存储的状态都是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地更新。
new Vuex.Store({
state: {
name: 'zhangsan',
sex: '男',
age:10
}
})
在组件中获取Vuex中的state有三种方式
- 最简单的方式是通过计算属性获取
// 创建一个 Counter 组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
- 通过从根组件注入的store
// 根组件,把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
const app = new Vue({
el: '#app',
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
//子组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
- 通过mapState辅助函数,mapState 函数返回的是一个对象
import { mapState } from 'vuex'
//1.基本用法
const Counter = {
template: `<div>{{ count }}</div>`,
computed: mapState({
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
})
}
//2.组件中的计算属性名和store中的state名一致时,可以给 mapState 传一个字符串数组
const Counter = {
template: `<div>{{ count }}</div>`,
computed: mapState(['count'])
}
//3.使用对象展开运算符
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
...mapState(['count'])
}
}
mutations
- 提交 mutation是更改 Vuex 的 store 中的状态的唯一方法
- mutation类似于事件,每个 mutation 都有字符串事件类型 (type) 和 回调函数 (handler)
- 每个mutation回调函数第一个参数必须是state(状态),第二个非必须参数payload(字符串或对象)
- 每个mutation必须是同步函数
提交mutation方式
//1.提交载荷
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10) //载荷是普通值
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', { amount: 10 })//载荷是对象
//2.对象风格提交
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit({ type: 'increment', amount: 10 })
actions
- action提交mutation,不能直接更改store中的状态
- 每个 action都有字符串事件类型 (type) 和 回调函数 (handler)
- action可以是同步函数,也可以是异步函数
通过store.dispatch()分发Actions
//1.不带参数分发
actions: {
increment ({ commit }) {
commit('increment')
}
}
store.dispatch('increment')
//2.带参数方式分发
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
store.dispatch('incrementAsync', { amount: 10 }) //以载荷形式分发
store.dispatch({ type: 'incrementAsync', amount: 10 }) //以对象形式分发
在组建中分发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')`
})
}
}
除了以上三个主要核心概念外,还有getters和modules。
Getters
- getters的作用类似于vue.js里的计算属性,返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
- getter接收state 作为其第一个参数,接收其他getter作为第二个参数。
访问getter的方式
//1.通过属性访问(getter会暴露为 store.getters 对象)
getters: {
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
//2.通过方法访问(让 getter 返回一个函数,来实现给 getter 传参)
getters: {
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2)
//3.通过mapGetters辅助函数
import { mapGetters } from 'vuex'
export default {
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
Modules
- 以免store过于庞大复杂,Vuex允许我们从上至下将 store 分割成模块,每个模块拥有自己的
state、mutation、action、getter、甚至是嵌套子模块。
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 的状态
- 模块的局部状态
模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
对于模块内部的action,context.state为局部状态,context.rootState为根节点状态
const moduleA = {
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
对于模块内部的getter,第三个参数是根节点状态
const moduleA = {
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}