Skip to content

✨ 状态管理库 👌

快速总览

  • 状态管理用于集中管理组件间共享的状态,避免多层级手动传递。
  • Vue 官方推荐使用 Pinia(替代 Vuex),API 更简单、类型推断更好。
  • 核心概念:store(数据仓库)、stategettersactions
  • 常见场景:跨页面共享数据、复杂业务状态、持久化与插件扩展。

状态管理库基本介绍

所谓状态管理库,就是用于管理一个应用中组件的状态的。

传统方式组件之间传递状态:

  • 父传子用 Props
  • 子传父用 Emit

这种方式存在的问题?

如果你的应用的规模一旦慢慢变大,那么不同层级之间组件的状态传递,就会变得非常的麻烦。

状态管理库如何解决这个问题的?

在状态管理库中,会有一个统一的地方(数据仓库)管理所有的状态,这个时候组件之间要进行状态的传递,只需要一个组件将状态提交到仓库,然后另一个组件从仓库获取最新的状态即可。

何时需要状态管理?

  • 层级较深、兄弟/跨页面组件频繁共享数据时。
  • 事件与数据流变复杂、调试成本高时。
  • 需要统一的行为日志、持久化、或与服务端状态同步时。

Vue 生态的状态管理库

目前,Vue 生态官方所推荐的状态管理库是 Pinia,这是目前最新的状态管理库,用于替代以前的 Vuex 的,因此我们也是以 Pinia 为主,介绍这个最新的状态管理库。

Pinia ,发音为 /piːnjʌ/,来源于西班牙语 piña 。意思为菠萝,表示与菠萝一样,由很多小块组成。在 Pinia 中,每个 Store 都是单独存在,一同进行状态管理。

Pinia 是由 Vue.js 团队成员开发,最初是在 2019 年 11 月左右作为一项实验性工作提出的,目的是为了使用 Composition API 重新设计 Vuex,探索 Vuex 下一次迭代会是什么样子。但是 Pinia 在设计之初就倾向于同时支持 Vue 2 和 Vue 3,并且不强制要求开发者使用组合式 API。在探索的过程中,Pinia 实现了 Vuex5 提案的大部分内容,于是就直接取而代之了。

目前 Vue 官方已经宣布 Pinia 就是新一代的 Vuex,但是为了尊重作者本人,名字保持不变,仍然叫做 Pinia。

与之前的 Vuex 相比,Pinia 提供了更简单的 API,更少的规范,以及 Composition-API 风格的 API 。更重要的是,与 TypeScript 一起使用具有可靠的类型推断支持。

Pinia 官网地址:https://pinia.vuejs.org/

对比之前的 Vuex,Pinia 具有如下的特点:

  1. mutations 不复存在。只有 state 、getters 、actions
  2. actions 中支持同步异步方法修改 state 状态
  3. 与 TypeScript 一起使用具有可靠的类型推断支持
  4. 不再有模块嵌套,只有 Store 的概念,Store 之间可以相互调用
  5. 支持插件扩展,可以非常方便实现本地存储等功能
  6. 更加轻量,压缩后体积只有 2kb 左右

快速入门(3 步)

  1. 安装依赖
bash
npm i pinia
  1. 在入口挂载 Pinia
js
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";

const app = createApp(App);
app.use(createPinia()).mount("#app");
  1. 创建与使用 Store

src/stores 目录创建数据仓库。命名约定常用 useXxxStore

js
// 选项式 Store
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
  },
});
js
// 组合式 Store(推荐与组合式 API 搭配)
import { defineStore } from "pinia";
import { ref, computed } from "vue";

export const useCounterStore = defineStore("counter", () => {
  const count = ref(0);
  const double = computed(() => count.value * 2);

  const increment = () => {
    count.value++;
  };
  const decrement = () => {
    count.value--;
  };

  return { count, double, increment, decrement };
});

通过 defineStore 方法来创建一个数据仓库:

  • 第一个参数为仓库名称(字符串标识,参与 DevTools 展示与持久化键名)。
  • 第二个参数为配置对象或 setup 回调,可定义 state/getters/actions 或以组合式方式返回。

最后在组件中使用 Store:

vue
<template>
  <div class="counter">
    <h1>计数器:{{ counterStore.count }}(x2: {{ counterStore.double }})</h1>
    <button @click="counterStore.increment">增加</button>
    <button @click="counterStore.decrement">减少</button>
  </div>
</template>

<script setup>
import { useCounterStore } from "./stores/useCounterStore.js";
const counterStore = useCounterStore();
</script>

<style scoped>
.counter {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
button {
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
}
</style>

目录与命名约定

  • Store 文件建议放在 src/stores,命名使用 useXxxStore
  • 选项式 Store 更接近 Vuex 心智模型,组合式 Store 与组合式 API 更自然。
  • 需要持久化可通过插件或手动 localStorage/IndexedDB;注意序列化与安全。

常见坑

  • 忘记在入口通过 app.use(createPinia()) 安装 Pinia。
  • 直接在组件外部使用 useXxxStore() 导致共享实例与测试困难(建议仅在 setup 或生命周期中使用)。
  • 重构路径后导入别名错误,建议保持一致的 @/stores 或相对路径规范。