# 什么是状态管理?
从技术上来讲,每一个vue组件实例都已经在管理它自己的响应式状态了,如以下的一个计数器组件示例。
<script setup>
import {ref} from 'vue'
const count = ref(0) // 状态
function increment() {
count.value++
}
</script>
<!-- 视图 -->
<template>{{ count }}</template>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
它是一个独立的单元,由以下几个部分组成
- 状态:驱动整个应用的数据源
- 视图:对状态的一种声明式映射
- 动作:状态根据用户在视图中的输入而做出相应变更的可能方式 为了保持状态的可维护性,vue遵循单向数据流的原则,即子组件无法修改父组件流动过来的值
然而当多个组件共享一个共同的状态时,就没有这么简单了,如
- 多个视图可能都依赖于同一个状态
- 不同视图中的动作可能需要更改同一部分状态。 对于情景1,一个可行的办法是将共享状态提升到共同的祖先组件上去,在通过props传递下去,然而这么做会使代码变得冗长,且会导致props深潜。 对于情景2,我们经常会发现自己会直接通过模板引用获取父子组件实例,或者通过触发事件尝试改变和同步多个状态的副本,但这些模式都较为脆弱,且会导致代码不可维护。
对于这些问题最简单的解决办法,就是抽出组件中的共享状态,放在一个全局单例中来管理。这样我们的组件树就变成了一个大的视图,而任何位置上的改动都可以访问其中的状态和触发动作。
以下是一个简单的示例
<!-- store.js -->
<!-- 为了确保改变状态的逻辑像状态本身一样集中,建议在store上定义方法,方法的名称要表达出行动的意图 -->
import {reactive} from 'vue'
export const store = reactive({
count: 0,
increment() {
this.count++
}
})
<!-- componentA.vue -->
<template>
<button @click='store.increment()'></button> // 这里事件之所以使用内联表达式,是确保this的指向正确
{{store}}
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
除了使用响应式对象作为一个store之外,也可以使用其他响应式api,甚至是一个组合式函数来返回一个全局或者局部的状态。