如何利用`Pinia`的`state`与`actions`进行状态管理？

admin
Mon, 08 Sep 2025 08:31:28 +0000
Vue, actions, pinia, state

Pinia state 与 actions 状态管理深度解析 大家好，今天我们来深入探讨 Pinia 中 state 和 actions 的使用，以及如何利用它们进行高效的状态管理。Pinia 作为 Vue.js 的一个轻量级状态管理库，以其简洁的 API、模块化的设计和 Typescript 的良好支持，越来越受到开发者的青睐。 本次分享将结合实际案例，详细讲解 state 如何定义和使用，actions 如何修改 state，以及它们之间如何协作，最终构建一个健壮且易于维护的状态管理方案。 1. Pinia 基础：Store 的创建与使用 在开始深入 state 和 actions 之前，我们需要先了解 Pinia 的基本概念：Store。一个 Store 相当于一个状态容器，包含 state、actions 和 getters（我们稍后会提到）。要创建一个 Store，我们需要使用 defineStore 函数。 import { defineStore } from 'pinia'; export const useCounterStore = defineStore('cou …

继续阅读“如何利用`Pinia`的`state`与`actions`进行状态管理？”

The post 如何利用`Pinia`的`state`与`actions`进行状态管理？ first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

Pinia state 与 actions 状态管理深度解析

大家好，今天我们来深入探讨 Pinia 中 state 和 actions 的使用，以及如何利用它们进行高效的状态管理。Pinia 作为 Vue.js 的一个轻量级状态管理库，以其简洁的 API、模块化的设计和 Typescript 的良好支持，越来越受到开发者的青睐。 本次分享将结合实际案例，详细讲解 state 如何定义和使用，actions 如何修改 state，以及它们之间如何协作，最终构建一个健壮且易于维护的状态管理方案。

1. Pinia 基础：Store 的创建与使用

在开始深入 state 和 actions 之前，我们需要先了解 Pinia 的基本概念：Store。一个 Store 相当于一个状态容器，包含 state、actions 和 getters（我们稍后会提到）。要创建一个 Store，我们需要使用 defineStore 函数。

import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
  },
});

在这个例子中，我们创建了一个名为 counter 的 Store。defineStore 的第一个参数是 Store 的唯一 ID，推荐使用有意义的名称。第二个参数是一个配置对象，包含 state 和 actions。

state: 一个返回对象的函数，用于定义 Store 的状态。注意，state 必须是一个函数，这允许 Pinia 在服务端渲染时创建独立的 Store 实例，避免数据污染。

actions: 一个对象，包含一些函数，用于修改 Store 的状态。

现在，我们可以在 Vue 组件中使用 useCounterStore 来访问和修改状态。


Count: {{ count }}
Increment
Decrement



import { useCounterStore } from './stores/counter';

const counterStore = useCounterStore();
const { count, increment, decrement } = counterStore; // 推荐使用解构赋值



这里，我们首先导入 useCounterStore 函数，然后调用它来获取 Store 实例。 接下来，我们解构 Store 实例，获取 count 状态和 increment 和 decrement action。 在模板中，我们使用 count 显示状态，并使用 increment 和 decrement action 来更新状态。

2. state：定义与访问

state 是 Store 的核心，用于存储应用程序的状态。正如前面提到的，state 必须是一个返回对象的函数。这确保了每个 Store 实例都有自己的状态副本。

2.1 基本数据类型

state 可以存储各种基本数据类型，例如：

state: () => ({
  count: 0,
  message: 'Hello, Pinia!',
  isLoggedIn: false,
}),

2.2 复杂数据类型

state 也可以存储复杂的数据类型，例如数组和对象。

state: () => ({
  items: [],
  user: {
    name: 'John Doe',
    email: 'john.doe@example.com',
  },
}),

2.3 使用 reactive 定义复杂 state

对于复杂的 state 结构，我们可以使用 Vue 的 reactive 函数来创建响应式对象。 这在处理嵌套对象或需要更细粒度控制时非常有用。

import { reactive } from 'vue';

state: () => {
  const data = reactive({
    profile: {
      name: 'Jane Doe',
      age: 30,
      address: {
        city: 'New York',
        country: 'USA'
      }
    },
    settings: {
      theme: 'light',
      notifications: true
    }
  });
  return {
    data
  };
},

在这种情况下，data 对象及其所有嵌套属性都是响应式的。 这意味着当 profile.name 或 settings.theme 发生变化时，Vue 组件会自动更新。

2.4 访问 state

在组件中，我们可以通过 Store 实例直接访问 state。


Count: {{ count }}
Message: {{ message }}
User Name: {{ user.name }}



import { useCounterStore } from './stores/counter';

const counterStore = useCounterStore();
const { count, message, user } = counterStore;


或者，我们也可以使用 storeToRefs 方法来解构 state，并保持响应性。


Count: {{ count }}
Message: {{ message }}
User Name: {{ user.name }}



import { useCounterStore } from './stores/counter';
import { storeToRefs } from 'pinia';

const counterStore = useCounterStore();
const { count, message, user } = storeToRefs(counterStore);


storeToRefs 会将 Store 的 state 转换为 ref 对象，这使得在组件中更容易使用和跟踪状态的变化。

3. actions：修改 state 的唯一途径

actions 是 Store 中用于修改 state 的函数。 它们是修改 state 的唯一途径，这有助于保持状态的可预测性和可维护性。

3.1 同步 actions

同步 actions 是最常见的 actions 类型。 它们立即修改 state。

actions: {
  increment() {
    this.count++;
  },
  setMessage(newMessage: string) {
    this.message = newMessage;
  },
  updateUser(newUser: { name: string; email: string }) {
    this.user = newUser;
  },
},

在 actions 中，我们可以使用 this 关键字来访问和修改 state。 我们可以传递参数给 actions，以便更灵活地修改 state。

3.2 异步 actions

异步 actions 用于处理异步操作，例如 API 请求。 它们可以返回 Promise。

import axios from 'axios';

actions: {
  async fetchUser() {
    try {
      const response = await axios.get('/api/user');
      this.user = response.data;
    } catch (error) {
      console.error('Failed to fetch user:', error);
    }
  },
  async saveSettings(settings: any) {
     try {
        await axios.post('/api/settings', settings);
        // 更新本地状态
        this.settings = settings;
        return true; // 成功返回true
     } catch(error){
        console.error("Failed to save settings:", error);
        return false; // 失败返回false
     }
  }
},

在异步 actions 中，我们使用 async 和 await 关键字来处理 Promise。 我们可以使用 try...catch 块来处理错误。 异步 action 最好能返回一个 Promise，这样组件可以知道异步操作何时完成，并且可以处理成功和失败的情况。

3.3 调用 actions

在组件中，我们可以通过 Store 实例调用 actions。


Increment
Set Message
Fetch User



import { useCounterStore } from './stores/counter';

const counterStore = useCounterStore();
const { increment, setMessage, fetchUser } = counterStore;


3.4 actions 之间的相互调用

一个 action 可以调用另一个 action。这有助于将复杂的逻辑分解成更小的、可重用的函数。

actions: {
  increment() {
    this.count++;
    this.logCount(); // 调用另一个 action
  },
  logCount() {
    console.log('Current count:', this.count);
  },
},

4. getters：派生状态

getters 用于从 state 派生新的状态。 它们类似于 Vue 的计算属性。

getters: {
  doubleCount: (state) => state.count * 2,
  userName: (state) => state.user.name,
  // Getter 也可以访问其他的 Getter
  formattedMessage: (state) => `Message: ${state.message}`,
  isAdult: (state) => {
    const age = state.user?.age;
    return age !== undefined && age >= 18;
  }
},

getters 接收 state 作为第一个参数。 它们应该返回一个值。

4.1 使用 getters

在组件中，我们可以通过 Store 实例访问 getters。


Double Count: {{ doubleCount }}
User Name: {{ userName }}



import { useCounterStore } from './stores/counter';

const counterStore = useCounterStore();
const { doubleCount, userName } = counterStore;


getters 是响应式的，这意味着当依赖的 state 发生变化时，getters 会自动更新。

4.2 getters 的缓存

getters 会被缓存，这意味着只有当依赖的 state 发生变化时，它们才会重新计算。 这可以提高性能，特别是对于计算量大的 getters。

4.3 向 getters 传递参数

虽然 getters 的主要目的是基于 state 计算派生值，但在某些情况下，你可能需要向 getters 传递参数以进行更灵活的计算。 为了实现这一点，你需要返回一个函数，该函数接受参数并执行计算。

getters: {
  getItemById: (state) => (id: number) => {
    return state.items.find(item => item.id === id);
  },
  filteredItems: (state) => (query: string) => {
    const lowerCaseQuery = query.toLowerCase();
    return state.items.filter(item => item.name.toLowerCase().includes(lowerCaseQuery));
  }
},

在这个例子中，getItemById getter 返回一个函数，该函数接受一个 id 参数并返回相应的 item。 在组件中，你可以这样使用它：


Item Name: {{ getItemName(1) }}



import { useCounterStore } from './stores/counter';

const counterStore = useCounterStore();
const { getItemById } = counterStore;

const getItemName = (id) => {
  const item = getItemById(id);
  return item ? item.name : 'Item not found';
};


5. 模块化 Store

随着应用程序的增长，将 Store 分成更小的、更易于管理的模块是很重要的。 Pinia 允许我们通过 defineStore 创建多个 Store，并将它们组合在一起。

// store/user.ts
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    name: 'John Doe',
    email: 'john.doe@example.com',
  }),
  actions: {
    updateName(newName: string) {
      this.name = newName;
    },
  },
});

// store/settings.ts
import { defineStore } from 'pinia';

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    theme: 'light',
    notifications: true,
  }),
  actions: {
    toggleTheme() {
      this.theme = this.theme === 'light' ? 'dark' : 'light';
    },
  },
});

然后，我们可以在组件中导入和使用这些模块化的 Store。


User Name: {{ userName }}
Theme: {{ theme }}
Update Name
Toggle Theme



import { useUserStore } from './stores/user';
import { useSettingsStore } from './stores/settings';

const userStore = useUserStore();
const settingsStore = useSettingsStore();
const { name: userName, updateName } = userStore;
const { theme, toggleTheme } = settingsStore;


模块化 Store 可以提高代码的可读性、可维护性和可重用性。

6. Pinia 与 Typescript

Pinia 对 Typescript 提供了良好的支持。 使用 Typescript 可以提高代码的健壮性和可维护性。

6.1 类型推断

Pinia 可以自动推断 state、actions 和 getters 的类型。 这可以减少代码中的类型声明。

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0, // 类型推断为 number
    message: 'Hello, Pinia!', // 类型推断为 string
  }),
  actions: {
    increment() {
      this.count++;
    },
    setMessage(newMessage: string) { // newMessage 类型推断为 string
      this.message = newMessage;
    },
  },
  getters: {
    doubleCount: (state) => state.count * 2, // 类型推断为 number
  },
});

6.2 显式类型声明

我们也可以显式地声明 state、actions 和 getters 的类型。 这可以提供更强的类型安全性和代码提示。

import { defineStore } from 'pinia';

interface User {
  name: string;
  email: string;
}

interface CounterState {
  count: number;
  message: string;
  user: User;
}

export const useCounterStore = defineStore<'counter', CounterState>('counter', {
  state: (): CounterState => ({
    count: 0,
    message: 'Hello, Pinia!',
    user: {
      name: 'John Doe',
      email: 'john.doe@example.com',
    },
  }),
  actions: {
    increment() {
      this.count++;
    },
    setMessage(newMessage: string) {
      this.message = newMessage;
    },
    updateUser(newUser: User) {
      this.user = newUser;
    },
  },
  getters: {
    doubleCount: (state: CounterState): number => state.count * 2,
    userName: (state: CounterState): string => state.user.name,
  },
});

通过显式类型声明，我们可以确保代码中的类型一致性，并减少运行时错误。

7. Pinia 插件

Pinia 插件可以扩展 Pinia 的功能。 它们可以用于添加日志记录、持久化存储、调试工具等。

7.1 创建插件

一个 Pinia 插件是一个函数，它接收 Pinia 实例作为参数。

import { PiniaPlugin } from 'pinia';

const myPlugin: PiniaPlugin = (context) => {
  console.log('Pinia plugin installed');

  context.store.$subscribe((mutation, state) => {
    console.log(`[${mutation.storeId}] ${mutation.type}:`, mutation.payload, state);
  });
};

export default myPlugin;

在这个例子中，我们创建了一个简单的 Pinia 插件，它在 Pinia 安装时记录一条消息，并在每次 state 发生变化时记录一条消息。

7.2 安装插件

要安装插件，我们需要使用 pinia.use() 方法。

import { createPinia } from 'pinia';
import myPlugin from './plugins/myPlugin';

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

// 创建 Vue 应用
import { createApp } from 'vue';
import App from './App.vue';

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

7.3 常用插件

pinia-plugin-persist: 用于持久化存储 Store 的 state。

@pinia/nuxt: 用于在 Nuxt.js 项目中使用 Pinia。

8. Pinia Devtools

Pinia 提供了官方的 Devtools 集成，可以方便地调试和检查状态。 通过 Pinia Devtools，你可以：

查看 Store 的 state、getters 和 actions。

时间旅行：撤销和重做 actions。

导入和导出 Store 的 state。

跟踪 actions 的调用。

Pinia Devtools 可以极大地提高开发效率和调试体验。 只需要在浏览器中安装 Vue Devtools 插件即可使用。

9. state 与 actions 协同工作：一个简单的购物车示例

让我们通过一个简单的购物车示例来演示 state 和 actions 如何协同工作。

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

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

interface CartState {
  items: CartItem[];
}

export const useCartStore = defineStore('cart', {
  state: (): CartState => ({
    items: [],
  }),
  actions: {
    addItem(item: { id: number; name: string; price: number }) {
      const existingItem = this.items.find((i) => i.id === item.id);
      if (existingItem) {
        existingItem.quantity++;
      } else {
        this.items.push({ ...item, quantity: 1 });
      }
    },
    removeItem(itemId: number) {
      this.items = this.items.filter((item) => item.id !== itemId);
    },
    updateQuantity(itemId: number, quantity: number) {
      const item = this.items.find((i) => i.id === itemId);
      if (item) {
        item.quantity = quantity;
      }
    },
  },
  getters: {
    totalPrice: (state) => {
      return state.items.reduce((total, item) => total + item.price * item.quantity, 0);
    },
    itemCount: (state) => {
      return state.items.reduce((count, item) => count + item.quantity, 0);
    },
  },
});

// components/ProductList.vue



      {{ product.name }} - ${{ product.price }}
Add to Cart





import { useCartStore } from '../stores/cart';

const cartStore = useCartStore();
const { addItem } = cartStore;

const products = [
  { id: 1, name: 'Product A', price: 10 },
  { id: 2, name: 'Product B', price: 20 },
  { id: 3, name: 'Product C', price: 30 },
];

const addToCart = (product) => {
  addItem(product);
};


// components/ShoppingCart.vue

Shopping Cart


      {{ item.name }} - ${{ item.price }} x {{ item.quantity }}
Remove


Total Price: ${{ totalPrice }}
Item Count: {{ itemCount }}



import { useCartStore } from '../stores/cart';

const cartStore = useCartStore();
const { items: cartItems, removeItem, totalPrice, itemCount } = cartStore;

const removeFromCart = (itemId) => {
  removeItem(itemId);
};


在这个示例中，useCartStore 管理购物车的状态。 state 存储购物车中的商品列表。 actions 用于添加、删除和更新购物车中的商品。 getters 用于计算总价和商品数量。 ProductList 组件显示商品列表，并允许用户将商品添加到购物车。 ShoppingCart 组件显示购物车中的商品，并允许用户删除商品。

10. Pinia 的优势与适用场景

Pinia 相较于 Vuex 等其他状态管理库，具有以下优势：

更轻量级: Pinia 的体积更小，API 更简洁。

更好的 Typescript 支持: Pinia 对 Typescript 提供了更好的支持，可以提高代码的健壮性和可维护性。

模块化设计: Pinia 允许将 Store 分成更小的、更易于管理的模块。

更少的样板代码: Pinia 需要更少的样板代码，可以提高开发效率。

Pinia 适用于以下场景：

中大型 Vue.js 应用程序。

需要集中式状态管理的应用程序。

需要可预测性和可维护性的应用程序。

需要 Typescript 支持的应用程序。

特性 | Pinia | Vuex
体积 | 更小 | 更大
Typescript | 更好的支持 | 支持，但不如 Pinia
模块化 | 更好的模块化设计 | 模块化设计，但更复杂
API | 更简洁 | 更复杂
学习曲线 | 更低 | 更高
适用场景 | 中大型 Vue.js 应用，需要轻量级解决方案 | 中大型 Vue.js 应用，历史悠久，生态完善

11. 总结：高效管理状态，构建健壮应用

今天我们深入探讨了 Pinia 中 state 和 actions 的使用。 掌握 state 的定义和访问、actions 的修改和调用，以及 getters 的派生状态，可以帮助我们构建健壮且易于维护的 Vue.js 应用程序。 模块化 Store 和 Pinia 插件可以进一步提高代码的可读性、可维护性和可扩展性。 希望今天的分享对大家有所帮助。

12. 通过 state 定义状态，actions 改变状态，getters 获取状态

state 定义应用所需的状态，actions 负责修改 state 中的数据，而 getters 则根据 state 计算派生数据。三者协同工作，构建起清晰可维护的状态管理体系。