pinia状态管理在vue3项目中的用法详解

Pinia 是 Vue3 官方推荐的状态管理库(替代 Vuex),相比 Vuex 更简洁、轻量化,原生支持组合式 API,无嵌套模块限制,且完美兼容 Vue DevTools。下面时pinia官方的介绍:

下面介绍一下 Pinia这款状态管理工具的完整用法,实际项目在的大多数场景可以直接照着代码改一下就可以用了。

一、核心优势(为什么选 Pinia)

  1. 无需嵌套模块,天然支持「扁平化模块化」;
  2. 抛弃 mutation(仅需 state/getters/actions),简化状态修改逻辑;
  3. 原生支持 TypeScript,类型提示更友好;
  4. 支持组合式 API(setup)和选项式 API 两种写法;
  5. 体积更小(约 1KB),性能更优;
  6. 完美兼容 Vue DevTools(支持时间旅行、状态追踪)。

二、基础安装

bash 复制代码
# npm
npm install pinia

# yarn
yarn add pinia

# pnpm
pnpm add pinia

三、全局挂载 Pinia 实例

main.js 中创建并挂载 Pinia 实例(所有组件可共享 Store):

javascript 复制代码
// src/main.js (Vue3 + Vite)
import { createApp } from 'vue';
import { createPinia } from 'pinia'; // 导入 Pinia
import App from './App.vue';

const app = createApp(App);
const pinia = createPinia(); // 创建 Pinia 实例
app.use(pinia); // 挂载到 Vue 应用
app.mount('#app');

四、定义 Store(核心)

Pinia 中「Store」是状态管理的核心单元,每个业务模块(如用户、购物车)对应一个独立的 Store。

支持两种写法:选项式 API(类似 Vuex)组合式 API(更灵活)

1. 选项式 API 写法(推荐新手)
javascript 复制代码
// src/stores/counterStore.js
import { defineStore } from 'pinia';

// 第一个参数:Store 唯一标识(必须唯一),第二个参数:配置对象
export const useCounterStore = defineStore('counter', {
  // 1. 状态:返回初始值的函数(类似 Vue 的 data)
  state: () => ({
    count: 0,
    title: 'Pinia 计数器',
    userInfo: { name: '张三', age: 20 }
  }),

  // 2. 计算属性:类似 Vue 的 computed,缓存结果
  getters: {
    // 基础用法:基于 state 计算
    doubleCount: (state) => state.count * 2,

    // 依赖其他 getter(通过 this 访问)
    doubleCountPlusOne() {
      return this.doubleCount + 1;
    },

    // 带参数的 getter(返回函数)
    getCountByMultiplier: (state) => (multiplier) => {
      return state.count * multiplier;
    }
  },

  // 3. 方法:支持同步/异步,修改状态(类似 Vue 的 methods)
  actions: {
    // 同步修改
    increment() {
      this.count++; // 直接修改 state,无需 mutation
    },

    // 带参数的同步方法
    incrementBy(num) {
      this.count += num;
    },

    // 异步方法(如接口请求)
    async fetchData() {
      const res = await fetch('https://api.example.com/count');
      const data = await res.json();
      this.count = data.count; // 异步修改状态
    },

    // 调用其他 action
    async fetchAndIncrement() {
      await this.fetchData(); // 调用自身异步 action
      this.increment(); // 调用同步 action
    }
  }
});
2. 组合式 API 写法(推荐 Vue3 组合式项目)

更灵活,可复用组合式函数(composables):

javascript 复制代码
// src/stores/counterStore.js
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

// 组合式 API 写法:返回函数
export const useCounterStore = defineStore('counter', () => {
  // 1. 状态:等价于 state(用 ref/reactive 定义)
  const count = ref(0);
  const title = ref('Pinia 计数器');
  const userInfo = ref({ name: '张三', age: 20 });

  // 2. 计算属性:等价于 getters(用 computed 定义)
  const doubleCount = computed(() => count.value * 2);
  const doubleCountPlusOne = computed(() => doubleCount.value + 1);
  const getCountByMultiplier = (multiplier) => count.value * multiplier;

  // 3. 方法:等价于 actions(普通函数)
  const increment = () => {
    count.value++;
  };
  const incrementBy = (num) => {
    count.value += num;
  };
  const fetchData = async () => {
    const res = await fetch('https://api.example.com/count');
    const data = await res.json();
    count.value = data.count;
  };

  // 必须返回需要暴露的状态/方法
  return {
    count,
    title,
    userInfo,
    doubleCount,
    doubleCountPlusOne,
    getCountByMultiplier,
    increment,
    incrementBy,
    fetchData
  };
});

五、组件中使用 Store

1. 基础使用(组合式 API 组件)
vue 复制代码
<!-- src/components/Counter.vue -->
<template>
  <div>
    <h3>{{ counterStore.title }}</h3>
    <p>当前计数:{{ counterStore.count }}</p>
    <p>双倍计数:{{ counterStore.doubleCount }}</p>
    <p>三倍计数:{{ counterStore.getCountByMultiplier(3) }}</p>

    <button @click="counterStore.increment">+1</button>
    <button @click="counterStore.incrementBy(5)">+5</button>
    <button @click="counterStore.fetchData">异步获取计数</button>
    <button @click="resetCount">重置计数</button>
  </div>
</template>

<script setup>
// 1. 导入 Store 函数
import { useCounterStore } from '@/stores/counterStore';

// 2. 创建 Store 实例(Pinia 会自动复用单例,多次调用也返回同一个实例)
const counterStore = useCounterStore();

// 3. 直接修改状态(Pinia 默认允许,可通过严格模式禁止)
const resetCount = () => {
  counterStore.count = 0;
};
</script>
2. 解构 Store 保持响应式(关键)

直接解构 Store 会丢失响应式,需用 storeToRefs 辅助:

vue 复制代码
<script setup>
import { useCounterStore } from '@/stores/counterStore';
import { storeToRefs } from 'pinia'; // 导入辅助函数

const counterStore = useCounterStore();

// 错误:解构后丢失响应式
// const { count, doubleCount } = counterStore;

// 正确:用 storeToRefs 解构,保留响应式
const { count, doubleCount, title } = storeToRefs(counterStore);

// actions 方法无需解构(本身是函数,不涉及响应式)
const { increment, fetchData } = counterStore;
</script>

<template>
  <div>
    <p>{{ title }}</p>
    <p>{{ count }}</p>
    <p>{{ doubleCount }}</p>
    <button @click="increment">+1</button>
    <button @click="fetchData">异步获取</button>
  </div>
</template>
3. 选项式 API 组件中使用
vue 复制代码
<script>
import { useCounterStore } from '@/stores/counterStore';
import { mapState, mapActions } from 'pinia'; // 导入映射辅助函数

export default {
  computed: {
    // 映射 state/getters 到计算属性
    ...mapState(useCounterStore, ['count', 'doubleCount', 'title'])
  },
  methods: {
    // 映射 actions 到方法
    ...mapActions(useCounterStore, ['increment', 'fetchData'])
  },
  mounted() {
    console.log(this.count); // 访问状态
    this.increment(); // 调用方法
  }
};
</script>

六、模块化(多 Store 管理)

Pinia 天然支持模块化,每个业务模块对应一个独立的 Store,无需嵌套:

复制代码
src/stores/
├── counterStore.js  // 计数器 Store
├── userStore.js     // 用户 Store
├── cartStore.js     // 购物车 Store
示例:用户 Store
javascript 复制代码
// src/stores/userStore.js
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: null,
    permissions: []
  }),
  actions: {
    // 登录
    async login(username, password) {
      const res = await fetch('/api/login', {
        method: 'POST',
        body: JSON.stringify({ username, password })
      });
      const data = await res.json();
      this.token = data.token;
      this.userInfo = data.userInfo;
      this.permissions = data.permissions;
    },
    // 退出登录
    logout() {
      this.token = '';
      this.userInfo = null;
      this.permissions = [];
    }
  }
});
组件中按需导入多 Store
vue 复制代码
<script setup>
import { useCounterStore } from '@/stores/counterStore';
import { useUserStore } from '@/stores/userStore';

const counterStore = useCounterStore();
const userStore = useUserStore();

// 调用不同 Store 的方法
const handleLogin = async () => {
  await userStore.login('admin', '123456');
  counterStore.increment();
};
</script>

七、进阶用法

1. 状态持久化(本地存储)

安装插件 pinia-plugin-persistedstate,可以实现 Store 状态自动持久化到 localStorage/sessionStorage

bash 复制代码
npm install pinia-plugin-persistedstate
javascript 复制代码
// src/main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; // 导入插件
import App from './App.vue';

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate); // 注册插件

const app = createApp(App);
app.use(pinia);
app.mount('#app');

在 Store 中开启持久化:

javascript 复制代码
// 选项式 API 写法
export const useUserStore = defineStore('user', {
  state: () => ({ token: '', userInfo: null }),
  actions: { /* ... */ },
  persist: true, // 开启持久化(默认存储到 localStorage)
});

// 自定义持久化配置
export const useCartStore = defineStore('cart', {
  state: () => ({ items: [] }),
  persist: {
    key: 'cart-store', // 自定义存储键名
    storage: sessionStorage, // 存储到 sessionStorage
    paths: ['items'], // 仅持久化 items 字段(默认全量)
  },
});
2. Store 间通信

一个 Store 可直接调用另一个 Store 的状态/方法:

javascript 复制代码
// src/stores/cartStore.js
import { defineStore } from 'pinia';
import { useUserStore } from './userStore'; // 导入其他 Store

export const useCartStore = defineStore('cart', {
  state: () => ({ items: [] }),
  actions: {
    async addItem(item) {
      const userStore = useUserStore(); // 创建其他 Store 实例
      if (!userStore.token) {
        throw new Error('未登录,无法添加商品');
      }
      // 调用接口添加商品
      await fetch('/api/cart/add', {
        method: 'POST',
        headers: { Authorization: userStore.token },
        body: JSON.stringify(item)
      });
      this.items.push(item);
    }
  }
});
3. 严格模式(禁止直接修改状态)

Pinia 默认允许直接修改 state,开启严格模式后,仅允许通过 actions 修改状态(类似 Vuex 的严格模式):

javascript 复制代码
// src/main.js
import { createPinia } from 'pinia';

const pinia = createPinia();

// 开启严格模式(仅生产环境建议关闭)
if (import.meta.env.MODE !== 'production') {
  pinia.use(({ store }) => {
    store.$subscribe((mutation, state) => {
      // 检测是否直接修改 state(非 actions 触发)
      if (mutation.type === 'direct') {
        console.warn(`[Pinia 严格模式] 禁止直接修改 ${store.$id} 的状态:${mutation.payload}`);
      }
    });
  });
}
4. 状态重置

调用 $reset() 方法重置 Store 到初始状态:

vue 复制代码
<script setup>
import { useCounterStore } from '@/stores/counterStore';

const counterStore = useCounterStore();

const resetStore = () => {
  counterStore.$reset(); // 重置所有状态到初始值
};
</script>
5. 批量修改状态

使用 $patch 批量修改状态(性能更优,DevTools 会记录为一次修改):

javascript 复制代码
// 方式1:对象形式
counterStore.$patch({
  count: 10,
  title: '新标题'
});

// 方式2:函数形式(支持复杂逻辑)
counterStore.$patch((state) => {
  state.count += 5;
  state.userInfo.age += 1;
});

八、调试技巧

  1. Vue DevTools:Pinia 完美兼容 DevTools,可在「Pinia」面板查看所有 Store 的状态,支持「时间旅行」(回溯状态修改记录);

  2. $subscribe 监听状态变化

    javascript 复制代码
    const counterStore = useCounterStore();
    // 监听状态变化
    counterStore.$subscribe((mutation, state) => {
      console.log('状态变化:', mutation.type, mutation.payload, state);
    });
  3. $onAction 监听 action 调用

    javascript 复制代码
    counterStore.$onAction(({ name, args, after, onError }) => {
      console.log(`开始执行 action:${name},参数:${args}`);
      after((result) => {
        console.log(`action ${name} 执行成功,结果:${result}`);
      });
      onError((error) => {
        console.error(`action ${name} 执行失败:${error}`);
      });
    });

九、常见问题

  1. 解构 Store 丢失响应式 :用 storeToRefs 解构状态,方法可直接解构;
  2. Store 实例重复创建 :无需担心,Pinia 会自动复用单例,多次调用 useCounterStore() 始终返回同一个实例;
  3. TypeScript 类型提示 :选项式 API 自动推导类型,组合式 API 需手动标注(或用 defineStore 自动推导);
  4. SSR 适配:Pinia 原生支持 SSR,需在服务端为每个请求创建独立的 Pinia 实例,避免状态污染。

总结

Pinia 的核心用法可总结为:

  1. 安装并挂载 Pinia 实例;
  2. defineStore 定义 Store(选项式/组合式);
  3. 组件中导入并创建 Store 实例,通过 storeToRefs 解构响应式状态;
  4. actions 处理同步/异步状态修改,getters 处理计算属性;
  5. 多 Store 实现模块化,插件扩展持久化、调试等能力。

相比 Vuex的繁琐,Pinia 更简洁、灵活,是 Vue3 项目状态管理的首选,建议在中大型项目中使用,小型项目可根据项目实际情况选择更加合适的。

相关推荐
恋猫de小郭13 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅20 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606121 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了21 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅21 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅21 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 天前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 天前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅1 天前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊1 天前
jwt介绍
前端