DukeDuke
主页
文档转换
关于我们
主页
文档转换
关于我们
  • 目录

    • Vue3简介
    • Vue3响应式系统
    • Vue3组合式API
    • Vue3生命周期
    • Vue3模板语法
    • Vue3组件系统
    • Vue3 路由系统
    • Vue3 状态管理
    • Vue3 性能优化
    • Vue3 TypeScript 支持
    • Vue3 项目实战
    • VUE 面试题大全
    • Node.js 安装

Vue3 状态管理 (Pinia) 详解

概述

Pinia 是 Vue3 推荐的状态管理库,它比 Vuex 更轻量、更类型安全。状态管理就像是应用的"大脑",集中管理所有组件需要共享的数据。

状态管理架构

基本概念

1. Store

Store 是 Pinia 的核心概念,它是一个包含状态和业务逻辑的实体。

// stores/counter.js
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", {
  // 状态
  state: () => ({
    count: 0,
    name: "计数器",
  }),

  // 计算属性
  getters: {
    doubleCount: (state) => state.count * 2,
    doubleCountPlusOne: (state) => state.count * 2 + 1,
  },

  // 方法
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
    incrementBy(amount) {
      this.count += amount;
    },
  },
});

2. 在组件中使用 Store

<template>
  <div>
    <h2>{{ counter.name }}</h2>
    <p>当前计数: {{ counter.count }}</p>
    <p>双倍计数: {{ counter.doubleCount }}</p>
    <p>双倍计数加一: {{ counter.doubleCountPlusOne }}</p>

    <button @click="counter.increment">增加</button>
    <button @click="counter.decrement">减少</button>
    <button @click="counter.incrementBy(5)">增加5</button>
  </div>
</template>

<script>
import { useCounterStore } from '@/stores/counter'

export default {
  setup() {
    const counter = useCounterStore()

    return {
      counter
    }
  }
}
</script>

Store 结构详解

1. State

State 是 Store 的数据源,类似于组件的 data。

export const useUserStore = defineStore("user", {
  state: () => ({
    // 基本类型
    name: "",
    age: 0,
    isLoggedIn: false,

    // 对象类型
    profile: {
      avatar: "",
      email: "",
      phone: "",
    },

    // 数组类型
    permissions: [],

    // 复杂对象
    settings: {
      theme: "light",
      language: "zh-CN",
      notifications: {
        email: true,
        push: false,
        sms: true,
      },
    },
  }),
});

2. Getters

Getters 类似于计算属性,基于 state 派生数据。

export const useUserStore = defineStore("user", {
  state: () => ({
    users: [
      { id: 1, name: "张三", age: 25, role: "admin" },
      { id: 2, name: "李四", age: 30, role: "user" },
      { id: 3, name: "王五", age: 28, role: "user" },
    ],
  }),

  getters: {
    // 基本 getter
    totalUsers: (state) => state.users.length,

    // 带参数的 getter
    getUserById: (state) => (id) => {
      return state.users.find((user) => user.id === id);
    },

    // 使用其他 getter
    adminUsers: (state) => state.users.filter((user) => user.role === "admin"),
    adminCount: (state, getters) => getters.adminUsers.length,

    // 复杂计算
    userStats: (state) => {
      const total = state.users.length;
      const admins = state.users.filter((user) => user.role === "admin").length;
      const users = total - admins;
      const avgAge =
        state.users.reduce((sum, user) => sum + user.age, 0) / total;

      return {
        total,
        admins,
        users,
        avgAge: Math.round(avgAge),
      };
    },
  },
});

3. Actions

Actions 用于处理异步操作和业务逻辑。

export const useUserStore = defineStore("user", {
  state: () => ({
    users: [],
    loading: false,
    error: null,
  }),

  actions: {
    // 异步获取用户列表
    async fetchUsers() {
      this.loading = true;
      this.error = null;

      try {
        const response = await fetch("/api/users");
        const data = await response.json();
        this.users = data;
      } catch (error) {
        this.error = error.message;
      } finally {
        this.loading = false;
      }
    },

    // 添加用户
    async addUser(userData) {
      try {
        const response = await fetch("/api/users", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(userData),
        });

        const newUser = await response.json();
        this.users.push(newUser);

        return newUser;
      } catch (error) {
        this.error = error.message;
        throw error;
      }
    },

    // 更新用户
    async updateUser(id, updates) {
      try {
        const response = await fetch(`/api/users/${id}`, {
          method: "PUT",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(updates),
        });

        const updatedUser = await response.json();
        const index = this.users.findIndex((user) => user.id === id);

        if (index !== -1) {
          this.users[index] = updatedUser;
        }

        return updatedUser;
      } catch (error) {
        this.error = error.message;
        throw error;
      }
    },

    // 删除用户
    async deleteUser(id) {
      try {
        await fetch(`/api/users/${id}`, {
          method: "DELETE",
        });

        const index = this.users.findIndex((user) => user.id === id);
        if (index !== -1) {
          this.users.splice(index, 1);
        }
      } catch (error) {
        this.error = error.message;
        throw error;
      }
    },

    // 重置状态
    resetState() {
      this.users = [];
      this.loading = false;
      this.error = null;
    },
  },
});

组合式 API 风格

Pinia 也支持组合式 API 的写法,使用 defineStore 的第二个参数为函数。

export const useCounterStore = defineStore("counter", () => {
  // 状态
  const count = ref(0);
  const name = ref("计数器");

  // 计算属性
  const doubleCount = computed(() => count.value * 2);
  const doubleCountPlusOne = computed(() => count.value * 2 + 1);

  // 方法
  function increment() {
    count.value++;
  }

  function decrement() {
    count.value--;
  }

  function incrementBy(amount) {
    count.value += amount;
  }

  return {
    count,
    name,
    doubleCount,
    doubleCountPlusOne,
    increment,
    decrement,
    incrementBy,
  };
});

Store 之间的交互

1. 在 Store 中使用其他 Store

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

  getters: {
    totalItems: (state) => state.items.length,
    totalPrice: (state) => {
      return state.items.reduce(
        (total, item) => total + item.price * item.quantity,
        0
      );
    },
  },

  actions: {
    addItem(product, quantity = 1) {
      // 使用用户 store 检查登录状态
      const userStore = useUserStore();

      if (!userStore.isLoggedIn) {
        throw new Error("请先登录");
      }

      const existingItem = this.items.find((item) => item.id === product.id);

      if (existingItem) {
        existingItem.quantity += quantity;
      } else {
        this.items.push({
          ...product,
          quantity,
        });
      }
    },
  },
});

2. 组合多个 Store

export const useAppStore = defineStore("app", () => {
  const userStore = useUserStore();
  const cartStore = useCartStore();
  const counterStore = useCounterStore();

  // 应用级别的状态
  const theme = ref("light");
  const language = ref("zh-CN");

  // 应用级别的计算属性
  const isReady = computed(() => {
    return userStore.isLoggedIn && !cartStore.loading;
  });

  // 应用级别的方法
  function resetAllStores() {
    userStore.resetState();
    cartStore.resetState();
    counterStore.resetState();
    theme.value = "light";
    language.value = "zh-CN";
  }

  return {
    theme,
    language,
    isReady,
    resetAllStores,
  };
});

持久化存储

1. 使用 pinia-plugin-persistedstate

// main.js
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

app.use(pinia);
export const useUserStore = defineStore("user", {
  state: () => ({
    token: "",
    userInfo: null,
  }),

  persist: {
    // 启用持久化
    enabled: true,

    // 指定要持久化的字段
    paths: ["token", "userInfo"],

    // 存储策略
    strategies: [
      {
        key: "user",
        storage: localStorage,
        paths: ["token", "userInfo"],
      },
      {
        key: "user-session",
        storage: sessionStorage,
        paths: ["token"],
      },
    ],
  },
});

2. 手动实现持久化

export const useSettingsStore = defineStore("settings", {
  state: () => ({
    theme: "light",
    language: "zh-CN",
    fontSize: 14,
  }),

  actions: {
    // 保存到本地存储
    saveToStorage() {
      localStorage.setItem(
        "settings",
        JSON.stringify({
          theme: this.theme,
          language: this.language,
          fontSize: this.fontSize,
        })
      );
    },

    // 从本地存储加载
    loadFromStorage() {
      const saved = localStorage.getItem("settings");
      if (saved) {
        const settings = JSON.parse(saved);
        this.theme = settings.theme || "light";
        this.language = settings.language || "zh-CN";
        this.fontSize = settings.fontSize || 14;
      }
    },

    // 更新设置并保存
    updateSettings(updates) {
      Object.assign(this, updates);
      this.saveToStorage();
    },
  },
});

开发工具支持

Pinia 提供了优秀的开发工具支持,可以在 Vue DevTools 中查看和调试 Store。

// 在开发环境中启用
if (process.env.NODE_ENV === "development") {
  const { devtools } = await import("pinia/devtools");
  devtools();
}

最佳实践

1. Store 设计原则

  • 按功能模块划分 Store
  • 保持 Store 的单一职责
  • 避免 Store 之间的循环依赖

2. 状态管理

  • 将共享状态放在 Store 中
  • 将组件内部状态保留在组件中
  • 合理使用 computed 和 watch

3. 异步操作

  • 在 actions 中处理异步操作
  • 使用 try-catch 处理错误
  • 提供加载状态和错误状态

4. 性能优化

export const useUserStore = defineStore("user", {
  state: () => ({
    users: [],
    userMap: new Map(), // 使用 Map 提高查找性能
  }),

  getters: {
    getUserById: (state) => (id) => {
      return (
        state.userMap.get(id) || state.users.find((user) => user.id === id)
      );
    },
  },

  actions: {
    setUsers(users) {
      this.users = users;
      // 构建 Map 索引
      this.userMap.clear();
      users.forEach((user) => this.userMap.set(user.id, user));
    },
  },
});

总结

Pinia 为 Vue3 提供了现代化、类型安全的状态管理解决方案。通过合理设计 Store 结构、使用 Getters 和 Actions、实现持久化存储等,可以构建出高效、可维护的状态管理系统。掌握这些概念和最佳实践,将帮助你构建出更好的 Vue 应用。

最近更新:: 2026/4/17 13:21
Contributors: Duke
Prev
Vue3 路由系统
Next
Vue3 性能优化