Pinia状态管理Skill pinia

Pinia 状态管理技能提供在 Vue 3 应用程序中实现高效、类型安全的状态管理解决方案。该技能涵盖 Pinia 存储的创建、异步操作、计算属性、插件集成和存储组合等核心功能,适用于前端开发中的状态管理、Vue 应用架构和复杂状态逻辑处理。关键词:Pinia, Vue 3, 状态管理, 前端开发, 存储模式, 持久化, 插件集成, 响应式数据。

前端开发 1 次安装 37 次浏览 更新于 2/26/2026

name: pinia description: 用于 Vue 3 的 Pinia 状态管理,包括存储创建、操作、getter、插件和 DevTools 集成。 allowed-tools: 读取、写入、编辑、Bash、Glob、Grep

Pinia 技能

为 Vue 3 应用程序实现 Pinia 状态管理的专家级协助。

能力

  • 创建类型安全的 Pinia 存储
  • 为异步操作实现 actions
  • 定义用于计算状态的 getters
  • 配置 Pinia 插件(持久化等)
  • 设置存储组合模式
  • 与 Vue DevTools 集成

使用场景

在以下情况时调用此技能:

  • 在 Vue 中设置全局状态管理
  • 创建特定于功能的存储
  • 实现持久化状态
  • 组合多个存储
  • 处理异步状态操作

输入参数

参数 类型 是否必需 描述
storeName 字符串 存储名称(使用前缀)
stateShape 对象 初始状态结构
actions 数组 存储操作
getters 数组 计算 getters
persist 布尔值 启用持久化

配置示例

{
  "storeName": "useUserStore",
  "stateShape": {
    "user": null,
    "isAuthenticated": false
  },
  "actions": ["login", "logout", "fetchUser"],
  "getters": ["fullName", "isAdmin"],
  "persist": true
}

存储模式

Setup 存储(推荐)

// stores/user.ts
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';

interface User {
  id: string;
  name: string;
  email: string;
  role: 'user' | 'admin';
}

export const useUserStore = defineStore('user', () => {
  // 状态
  const user = ref<User | null>(null);
  const loading = ref(false);
  const error = ref<string | null>(null);

  // Getters
  const isAuthenticated = computed(() => !!user.value);
  const isAdmin = computed(() => user.value?.role === 'admin');
  const fullName = computed(() => user.value?.name ?? 'Guest');

  // Actions
  async function login(email: string, password: string) {
    loading.value = true;
    error.value = null;

    try {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });

      if (!response.ok) {
        throw new Error('无效凭证');
      }

      const data = await response.json();
      user.value = data.user;
      localStorage.setItem('token', data.token);
    } catch (e) {
      error.value = (e as Error).message;
      throw e;
    } finally {
      loading.value = false;
    }
  }

  async function logout() {
    user.value = null;
    localStorage.removeItem('token');
  }

  async function fetchUser() {
    const token = localStorage.getItem('token');
    if (!token) return;

    loading.value = true;
    try {
      const response = await fetch('/api/auth/me', {
        headers: { Authorization: `Bearer ${token}` },
      });
      user.value = await response.json();
    } catch (e) {
      logout();
    } finally {
      loading.value = false;
    }
  }

  return {
    // 状态
    user,
    loading,
    error,
    // Getters
    isAuthenticated,
    isAdmin,
    fullName,
    // Actions
    login,
    logout,
    fetchUser,
  };
});

Options 存储

// stores/cart.ts
import { defineStore } from 'pinia';

interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [] as CartItem[],
  }),

  getters: {
    totalItems: (state) =>
      state.items.reduce((sum, item) => sum + item.quantity, 0),

    totalPrice: (state) =>
      state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),

    isEmpty: (state) => state.items.length === 0,
  },

  actions: {
    addItem(item: Omit<CartItem, 'quantity'>) {
      const existing = this.items.find((i) => i.id === item.id);
      if (existing) {
        existing.quantity++;
      } else {
        this.items.push({ ...item, quantity: 1 });
      }
    },

    removeItem(id: string) {
      const index = this.items.findIndex((i) => i.id === id);
      if (index > -1) {
        this.items.splice(index, 1);
      }
    },

    updateQuantity(id: string, quantity: number) {
      const item = this.items.find((i) => i.id === id);
      if (item) {
        item.quantity = Math.max(0, quantity);
        if (item.quantity === 0) {
          this.removeItem(id);
        }
      }
    },

    clearCart() {
      this.items = [];
    },
  },
});

Pinia 设置与插件

// main.ts
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');

// 带持久化的存储
export const useSettingsStore = defineStore('settings', {
  state: () => ({
    theme: 'light',
    language: 'en',
  }),
  persist: true, // 持久化到 localStorage
});

// 自定义持久化配置
export const useUserStore = defineStore('user', {
  state: () => ({
    user: null,
    token: null,
  }),
  persist: {
    key: 'user-store',
    storage: sessionStorage,
    paths: ['token'], // 仅持久化 token
  },
});

存储组合

// stores/checkout.ts
import { defineStore } from 'pinia';
import { useCartStore } from './cart';
import { useUserStore } from './user';

export const useCheckoutStore = defineStore('checkout', () => {
  const cart = useCartStore();
  const user = useUserStore();

  const canCheckout = computed(() => {
    return user.isAuthenticated && !cart.isEmpty;
  });

  async function processCheckout(paymentMethod: string) {
    if (!canCheckout.value) {
      throw new Error('无法结账');
    }

    const order = {
      userId: user.user!.id,
      items: cart.items,
      total: cart.totalPrice,
      paymentMethod,
    };

    const response = await fetch('/api/orders', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(order),
    });

    if (response.ok) {
      cart.clearCart();
    }

    return response.json();
  }

  return {
    canCheckout,
    processCheckout,
  };
});

在组件中使用

<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/stores/user';

const userStore = useUserStore();

// 解构并保持响应性
const { user, isAuthenticated, loading } = storeToRefs(userStore);

// Actions 可以直接解构
const { login, logout } = userStore;

async function handleLogin() {
  try {
    await login(email.value, password.value);
    router.push('/dashboard');
  } catch (e) {
    // 处理错误
  }
}
</script>

<template>
  <div v-if="loading">加载中...</div>
  <div v-else-if="isAuthenticated">
    欢迎,{{ user?.name }}
    <button @click="logout">登出</button>
  </div>
  <LoginForm v-else @submit="handleLogin" />
</template>

最佳实践

  • 推荐使用 setup 存储以获得更好的 TypeScript 支持
  • 使用 storeToRefs 进行响应式解构
  • 为复杂功能组合存储
  • 保持存储专注于单一职责
  • 使用插件处理横切关注点

目标流程

  • vue-application-development
  • nuxt-full-stack
  • state-management-setup
  • frontend-architecture