引言
在现代前端开发中,状态管理是构建复杂应用时不可或缺的一部分。随着应用程序的增长,组件之间的数据共享和通信变得越来越复杂。Vue.js 作为一个流行的前端框架,其生态系统也在不断发展,以应对这些挑战。Pinia 就是在这样的背景下应运而生的,它为 Vue 3 提供了一种简洁、直观且功能强大的状态管理解决方案。
什么是 Pinia?
Pinia 是专门为 Vue 3 设计的状态管理库,它继承了 Vuex(Vue 2 中的状态管理库)的优点,并通过简化 API 和增强 TypeScript 支持,提供了更现代化的解决方案。Pinia 的核心理念是让开发者能够更轻松地管理应用中的状态,同时保持代码的清晰性和可维护性。
- 集中式状态管理:所有状态都集中在 store 中,确保状态的一致性和可预测性。
- 简洁的 API:不再区分 mutations 和 actions,所有操作都可以在 actions 中完成。
- 更好的 TypeScript 支持:自动推断类型,减少手动声明类型的需要。
- 与 Composition API 完美兼容:充分利用 Vue 3 的新特性,提供更直观的状态管理方式。
- 插件系统和调试工具集成:支持插件扩展,并默认集成了 Vue Devtools。
如何使用Pinia
1. 搭建pinia环境
第一步:npm install pinia
第二步:操作src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
//引入createPinia,用于创建pinia
import {createPinia} from 'pinia'
//状态管理库
//pinia 的实例 vue 全家桶中的Store
const pinia = createPinia()
const app = createApp(App)
//使用插件
app
.use(pinia)
.mount('#app')
2.创建store
-
Store是一个保存:状态、业务逻辑 的实体,每个组件都可以读取、写入它。 -
defineStore是 Pinia 提供的一个函数,用于定义一个新的 store。它接受两个参数:-
- Store ID (标识符) : 一个字符串,作为 store 的唯一标识符。这个 ID 应该在整个应用中是唯一的,因为它是用来区分不同 store 的。当你在组件中使用
useStore()来获取 store 实例时,这个 ID 就是用来匹配相应的 store。
- Store ID (标识符) : 一个字符串,作为 store 的唯一标识符。这个 ID 应该在整个应用中是唯一的,因为它是用来区分不同 store 的。当你在组件中使用
-
- Store 定义对象: 这是一个包含了 state、getters 和 actions 的对象。它定义了 store 的行为和状态管理逻辑。这个对象可以以两种方式提供:
- 对象选项
- 回调函数
-
-
在
src目录下创建store目录来存储store仓库
使用对象选项
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
});
-
- State(状态)
State 是 store 的数据源,它存储了应用中需要被共享的状态信息。你可以将 state 看作是一个容器,用来存放所有你需要在整个应用中访问的数据。
- 2. Getters(获取器)
Getters 类似于 Vue 组件中的计算属性,它允许你从 state 中派生出一些新值,但这些值本身并不直接修改原始状态。Getters 主要用于处理依赖于 state 的逻辑计算或过滤。
-
- Actions(动作)
Actions 是用来定义改变 state 方法的地方。与 Vuex 不同的是,在 Pinia 中 actions 可以直接修改 state,而不需要通过 mutations 来间接变更。
使用回调函数
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', () => {
// 状态 (state)
const count = ref(0);
// 可以从 state 中计算值得到 getters
const doubleCount = computed(() => count.value * 2);
// 方法 (actions)
function increment() {
count.value++;
}
return { count, doubleCount, increment };
});
3.组件中使用store
<template>
<div>
<h2>CompA</h2>
<p>count:{{ counterStore.count }}</p>
<p>doubleCount:{{ counterStore.doubleCount }}</p>
<button @click="counterStore.increment">Add</button>
</div>
</template>
<script setup>
import { useCounterStore } from '../store/counter';
const counterStore = useCounterStore();
</script>
除了借助
action修改数据(action中可以编写一些业务逻辑),还有两种常用方式
- 直接修改
countStore.count +=1
- 批量修改
countStore.$patch({
count1:999,
count2:222
})
storeToRefs
- 借助
storeToRefs将store中的数据转为ref对象,方便在模板中使用。 - 注意:
pinia提供的storeToRefs只会将数据做转换,而Vue的toRefs会转换store中的所有属性。
<template>
<div>
<h2>CompA</h2>
<p>count:{{ count }}</p>
<p>doubleCount:{{ doubleCount }}</p>
<button @click="counterStore.increment">Add</button>
</div>
</template>
<script setup>
import { useCounterStore } from '../store/counter';
import { storeToRefs } from 'pinia';
import {toRefs} from 'vue';
const counterStore = useCounterStore();
const { count, doubleCount } = storeToRefs(counterStore);
console.log(storeToRefs(counterStore))
console.log(toRefs(counterStore))
</script>
总结Pinia的作用
- 集中式状态管理
Pinia 提供了一种集中式状态管理模式,允许我们将应用中的状态集中存放在一个或多个 store 中。这种模式有助于保持代码的清晰度,并确保状态的一致性和可预测性。每个 store 是独立的,但它们可以相互关联,共同构成整个应用的状态图。
-
组件关系与共享数据状态
父组件持有数据状态并共享给子组件
在 Vue 应用中,父子组件间的通信通常通过 props 和事件(如 v-on)来完成。父组件可以通过 props 向子组件传递数据,而子组件则可以通过自定义事件通知父组件更改状态。然而,当涉及到多个层级的嵌套组件时,这种方式可能会导致“prop-drilling”问题,即数据需要一层层传递下去,增加了维护成本。
使用 Pinia 可以有效解决这个问题。父组件不再需要直接持有所有数据状态,而是可以通过引入 store 来管理这些状态,并将 store 实例传递给子组件。这样一来,任何组件都可以访问到最新的状态,而不需要关心它是如何传递下来的。
<template>
<div>
<!-- 子组件可以直接访问 store -->
<ChildComponent />
</div>
</template>
<script setup>
import { useMainStore } from './stores/main';
const mainStore = useMainStore();
</script>
确保组件共享正确的数据状态
Pinia 保证了所有组件看到的是同一个 store 实例,因此无论组件位于何处,它们都能获取到最新且一致的状态。这大大简化了多组件协作时的状态同步问题。
兄弟组件不能直接修改 props 数据状态
兄弟组件之间不应该通过 props 直接修改对方的数据状态。这样做会导致数据流向不明确,难以追踪和调试。相反,兄弟组件应该通过共享的 store 来改变状态。store 的 actions 提供了安全的方法来更新状态,确保所有变更都是可控的。
// 正确的做法:通过 store 修改状态
<button @click="mainStore.increment">Increment</button>
- 超越父子组件通信,实现全局状态共享
除了处理父子组件间的通信外,Pinia 还能够轻松实现跨层级甚至非父子关系组件之间的状态共享。无论是顶部导航栏、侧边栏还是模态框,只要这些组件需要访问相同的数据或触发相同的逻辑,都可以通过引入相同的 store 来完成。
javascript
深色版本
// 在任意组件中使用相同的 store
import { useMainStore } from './stores/main';
export default {
setup() {
const mainStore = useMainStore();
return {
count: computed(() => mainStore.count),
increment: () => mainStore.increment(),
};
},
};
此外,Pinia 还支持插件系统,允许你扩展其功能,比如持久化存储、日志记录等。这对于需要在页面刷新后保留状态的应用非常有用。