外观
状态管理
什么是状态管理?
技术上,每个Vue组件实例都已经“管理”了自己的响应式状态。以一个简单的计数器组件为例
vue
<script setup>
import { ref } from 'vue'
// state
const count = ref(0)
// actions
function increment() {
count.value++
}
</script>
<!-- view -->
<template>{{ count }}</template>
它是一个自包含的单元,包括以下部分
- 状态,驱动我们应用的真相来源;
- 视图,对状态的声明性映射;
- 动作,在视图对用户输入的反应中可能改变状态的方式。
这是“单向数据流”概念的简单表示
然而,当我们有多个共享公共状态的组件时,这种简单性开始分解
- 多个视图可能依赖于同一状态片段。
- 来自不同视图的动作可能需要更改同一状态片段。
对于第一种情况,一个可能的解决方案是将共享状态“提升”到公共祖先组件,然后作为props传递下去。然而,在具有深层层次结构的组件树中,这很快就会变得繁琐,导致另一个被称为Prop Drilling的问题。
对于第二种情况,我们经常发现自己求助于直接通过模板引用访问父/子实例,或者尝试通过事件发射同步多个状态副本。这两种模式都很脆弱,很快就会导致难以维护的代码。
一个更简单、更直接的方法是将共享状态从组件中提取出来,并在全局单例中管理它。这样,我们的组件树就变成了一个大“视图”,无论它们在树中的位置如何,任何组件都可以访问状态或触发动作!
使用响应式API进行简单状态管理
如果你有一块应该由多个实例共享的状态,你可以使用reactive()
创建一个响应式对象,然后将其导入多个组件
js
// store.js
import { reactive } from 'vue'
export const store = reactive({
count: 0
})
vue
<!-- ComponentA.vue -->
<script setup>
import { store } from './store.js'
</script>
<template>From A: {{ store.count }}</template>
vue
<!-- ComponentB.vue -->
<script setup>
import { store } from './store.js'
</script>
<template>From B: {{ store.count }}</template>
现在每当 store
对象被修改时,<ComponentA>
和 <ComponentB>
将会自动更新它们的视图 - 现在我们有一个单一的真实来源。
然而,这也意味着任何导入 store
的组件都可以随意修改它
模板
<template>
<button @click="store.count++">
From B: {{ store.count }}
</button>
</template>
虽然在简单情况下这种方法是可行的,但任何组件都可以任意修改的全局状态在长期来看将难以维护。为了确保状态修改逻辑与状态本身一样集中,建议在 store 上定义具有表达动作意图的名称的方法
js
// store.js
import { reactive } from 'vue'
export const store = reactive({
count: 0,
increment() {
this.count++
}
})
模板
<template>
<button @click="store.increment()">
From B: {{ store.count }}
</button>
</template>
提示
注意点击处理器使用了 store.increment()
带括号 - 由于它不是一个组件方法,所以这是调用方法时使用正确的 this
上下文的必要条件。
尽管这里我们使用单个响应对象作为 store,您也可以共享使用其他 响应性 API(如 ref()
或 computed()
)创建的响应状态,甚至可以从 组合式 返回全局状态。
js
import { ref } from 'vue'
// global state, created in module scope
const globalCount = ref(1)
export function useCount() {
// local state, created per-component
const localCount = ref(1)
return {
globalCount,
localCount
}
}
Vue 的响应性系统与组件模型解耦,使其非常灵活。
SSR 考虑事项
如果您正在构建利用 服务器端渲染 (SSR) 的应用程序,上述模式可能会因为 store 是一个单例并在多个请求之间共享而引发问题。有关详细信息,请参阅 SSR 指南中的 更多内容。
Pinia
虽然我们自制的状态管理解决方案在简单场景下可以满足需求,但在大规模生产应用程序中还有更多需要考虑的事情。
- 更强的团队协作规范
- 与 Vue DevTools 的集成,包括时间轴、组件内检查和时间旅行调试
- 热模块替换
- 服务器端渲染支持
Pinia 是一个状态管理库,它实现了上述所有功能。它由 Vue 核心团队维护,并与 Vue 2 和 Vue 3 兼容。
现有用户可能对 Vuex 已经很熟悉,它是 Vue 的前一官方状态管理库。由于 Pinia 在生态系统中扮演相同的角色,Vuex 现在处于维护模式。它仍然可以工作,但将不再接收新功能。建议为新应用程序使用 Pinia。
Pinia 最初是对 Vuex 的下一版本可能是什么样的探索,结合了核心团队对 Vuex 5 的许多想法。最终,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的很多东西,并决定将其作为新推荐。
与 Vuex 相比,Pinia 提供了一个更简单、仪式感更少的 API,提供了 Composition-API 风格的 API,最重要的是,当与 TypeScript 一起使用时,具有坚实的类型推断支持。