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

admin
Mon, 08 Sep 2025 08:20:39 +0000
Vue, pinia, state, store

Pinia Store 与 State：深入解析与应用 大家好，今天我们来深入探讨 Pinia 中 store 与 state 的状态管理机制。Pinia 作为 Vue.js 的官方推荐状态管理库，以其轻量级、类型安全、模块化和开发体验友好等特点，受到了广泛的欢迎。本次讲座将从基础概念入手，逐步深入到高级应用，并通过大量的代码示例，帮助大家彻底理解和掌握 Pinia 中 store 和 state 的用法。 1. Pinia Store 基础：定义与创建 Pinia 的核心概念是 store。可以将 store 理解为一个应用中状态的容器，它包含应用所需的状态（state）、读取状态的方式（getters）以及修改状态的方式（actions）。 定义一个 store 通常使用 defineStore 函数。defineStore 接受两个参数：第一个参数是 store 的唯一 ID，用于在应用中区分不同的 store；第二个参数是一个选项对象，用于定义 store 的 state、getters 和 actions。 import { defineStore } from 'pinia …

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

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

Pinia Store 与 State：深入解析与应用

大家好，今天我们来深入探讨 Pinia 中 store 与 state 的状态管理机制。Pinia 作为 Vue.js 的官方推荐状态管理库，以其轻量级、类型安全、模块化和开发体验友好等特点，受到了广泛的欢迎。本次讲座将从基础概念入手，逐步深入到高级应用，并通过大量的代码示例，帮助大家彻底理解和掌握 Pinia 中 store 和 state 的用法。

1. Pinia Store 基础：定义与创建

Pinia 的核心概念是 store。可以将 store 理解为一个应用中状态的容器，它包含应用所需的状态（state）、读取状态的方式（getters）以及修改状态的方式（actions）。

定义一个 store 通常使用 defineStore 函数。defineStore 接受两个参数：第一个参数是 store 的唯一 ID，用于在应用中区分不同的 store；第二个参数是一个选项对象，用于定义 store 的 state、getters 和 actions。

import { defineStore } from 'pinia'

// 定义一个名为 counter 的 store
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
    incrementBy(amount: number) {
      this.count += amount
    }
  }
})

上述代码定义了一个名为 counter 的 store。

state 是一个函数，返回一个包含 count 和 name 属性的对象。这是 store 的初始状态。注意，state 必须是一个函数，这允许 Pinia 在不同的组件实例之间创建独立的 store 实例。

getters 是一个对象，包含 doubleCount 属性。getters 用于从 state 中派生出新的值，并且具有缓存机制，只有当依赖的 state 发生变化时才会重新计算。

actions 是一个对象，包含 increment、decrement 和 incrementBy 属性。actions 用于修改 state，并且可以包含异步操作。在 actions 中，可以使用 this 访问 store 的 state、getters 和其他 actions。

2. State 的核心：数据存储与响应式

state 是 store 的核心，用于存储应用程序的状态数据。state 必须是一个函数，并且返回一个对象。Pinia 使用 Vue 3 的响应式系统来追踪 state 的变化，当 state 发生变化时，依赖于该 state 的组件会自动更新。

State 的类型定义：

为了更好地管理 state，建议使用 TypeScript 来定义 state 的类型。

interface CounterState {
  count: number;
  name: string;
}

export const useCounterStore = defineStore('counter', {
  state: (): CounterState => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  // ...其他配置
})

通过定义 CounterState 接口，可以确保 state 中数据的类型安全。

State 的访问与修改：

在组件中，可以使用 useCounterStore 函数来获取 store 实例，并访问 store 的 state。



Count: {{ counterStore.count }}
Name: {{ counterStore.name }}
Increment




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

const counterStore = useCounterStore()


可以直接通过 counterStore.count 和 counterStore.name 访问 state 中的数据。通过调用 counterStore.increment() 方法，可以修改 state 中的 count 属性。

State 的替换：

Pinia 提供了 $reset 方法，用于将 state 恢复到初始状态。

const counterStore = useCounterStore()

counterStore.count = 10
counterStore.$reset() // 将 count 恢复到 0

Pinia 还提供了 $patch 方法，用于批量更新 state。$patch 接受一个对象或一个函数作为参数。如果参数是一个对象，则会将该对象中的属性合并到 state 中。如果参数是一个函数，则会将 state 作为参数传递给该函数，并在函数中修改 state。

const counterStore = useCounterStore()

// 使用对象更新 state
counterStore.$patch({
  count: 20,
  name: 'Updated Counter'
})

// 使用函数更新 state
counterStore.$patch((state) => {
  state.count += 5
  state.name = 'Function Updated Counter'
})

$patch 方法可以确保 state 的更新是响应式的。

State 的持久化：

默认情况下，store 的 state 只在当前浏览器会话中有效。当关闭浏览器或刷新页面时，state 会丢失。如果需要持久化 state，可以使用 Pinia 的插件，例如 pinia-plugin-persistedstate。

npm install pinia-plugin-persistedstate

// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

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

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

// store.ts
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  // ...其他配置
  persist: true // 启用持久化
})

通过配置 persist: true，可以将 store 的 state 持久化到 localStorage 中。

3. Getters 的妙用：派生状态与缓存

getters 用于从 state 中派生出新的值。getters 类似于 Vue 组件的计算属性，具有缓存机制，只有当依赖的 state 发生变化时才会重新计算。

Getters 的定义：

getters 是一个对象，包含多个函数。每个函数接受 state 作为参数，并返回一个新的值。

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  getters: {
    doubleCount: (state) => state.count * 2,
    greeting: (state) => `Hello, ${state.name}!`
  },
  // ...其他配置
})

上述代码定义了两个 getters：doubleCount 和 greeting。

Getters 的访问：

在组件中，可以通过 store 实例访问 getters。



Double Count: {{ counterStore.doubleCount }}
Greeting: {{ counterStore.greeting }}




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

const counterStore = useCounterStore()


Getters 的高级用法：

getters 还可以访问其他 getters。

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  getters: {
    doubleCount: (state) => state.count * 2,
    tripleCount: (state) => this.doubleCount * 1.5, // 访问 doubleCount
    greeting: (state) => `Hello, ${state.name}!`
  },
  // ...其他配置
})

上述代码中，tripleCount 访问了 doubleCount。注意，在 getters 中访问其他 getters 时，需要使用 this 关键字。

Getters 的参数化：

getters 还可以接受参数。

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  getters: {
    getCountPlus: (state) => (amount: number) => state.count + amount,
    greeting: (state) => `Hello, ${state.name}!`
  },
  // ...其他配置
})



Count Plus 5: {{ counterStore.getCountPlus(5) }}




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

const counterStore = useCounterStore()


上述代码中，getCountPlus 接受一个 amount 参数，并返回 count + amount 的值。

4. Actions 的力量：状态修改与异步操作

actions 用于修改 state。actions 可以包含同步操作和异步操作。

Actions 的定义：

actions 是一个对象，包含多个函数。每个函数可以接受任意数量的参数。

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  actions: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
    incrementBy(amount: number) {
      this.count += amount
    },
    async fetchCount() {
      const response = await fetch('/api/count')
      const data = await response.json()
      this.count = data.count
    }
  }
})

上述代码定义了四个 actions：increment、decrement、incrementBy 和 fetchCount。fetchCount 是一个异步 action，用于从服务器获取 count 的值。

Actions 的调用：

在组件中，可以通过 store 实例调用 actions。



Increment
Fetch Count




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

const counterStore = useCounterStore()


Actions 的 this 上下文：

在 actions 中，可以使用 this 访问 store 的 state、getters 和其他 actions。

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Pinia Counter'
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    incrementAndDouble() {
      this.increment()
      console.log('Double Count:', this.doubleCount) // 访问 getter
    },
    increment() {
      this.count++
    },
  }
})

Actions 的解构：

可以使用 storeToRefs 函数解构 store 的 state 和 getters，以便在组件中更方便地使用它们。



Count: {{ count }}
Double Count: {{ doubleCount }}
Increment




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

const counterStore = useCounterStore()
const { count, doubleCount } = storeToRefs(counterStore)
const { increment } = counterStore


storeToRefs 函数会将 state 和 getters 转换为 ref 对象，以便在组件中进行响应式追踪。

5. Store 的模块化：组织与复用

Pinia 支持 store 的模块化，可以将一个大型应用的状态拆分成多个独立的 store，以便更好地组织和复用代码。

Store 的组合：

可以使用 defineStore 函数定义多个 store，并在组件中分别使用它们。

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

export const useUserStore = defineStore('user', {
  state: () => ({
    name: 'John Doe',
    age: 30
  }),
  // ...其他配置
})

// store/product.ts
import { defineStore } from 'pinia'

export const useProductStore = defineStore('product', {
  state: () => ({
    products: [] as any[],
    loading: false
  }),
  // ...其他配置
})



User Name: {{ userStore.name }}
Product Count: {{ productStore.products.length }}




import { useUserStore } from './stores/user'
import { useProductStore } from './stores/product'

const userStore = useUserStore()
const productStore = useProductStore()


Store 的嵌套：

一个 store 还可以使用另一个 store。

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

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [] as any[]
  }),
  actions: {
    addItem(productId: number) {
      const productStore = useProductStore()
      const product = productStore.products.find(p => p.id === productId)
      if (product) {
        this.items.push(product)
      }
    }
  }
})

上述代码中，useCartStore 使用了 useProductStore 来获取商品信息。

Store 的复用：

可以将 store 的逻辑提取到函数中，以便在多个 store 中复用。

// utils/useCommonStoreLogic.ts
import { defineStore } from 'pinia'

export function useCommonStoreLogic(id: string) {
  return defineStore(id, {
    state: () => ({
      loading: false,
      error: null as string | null
    }),
    actions: {
      async fetchData(url: string) {
        this.loading = true
        this.error = null
        try {
          const response = await fetch(url)
          const data = await response.json()
          this.loading = false
          return data
        } catch (error:any) {
          this.loading = false
          this.error = error.message
          return null
        }
      }
    }
  })
}

// store/user.ts
import { useCommonStoreLogic } from '../utils/useCommonStoreLogic'

export const useUserStore = useCommonStoreLogic('user')

// store/product.ts
import { useCommonStoreLogic } from '../utils/useCommonStoreLogic'

export const useProductStore = useCommonStoreLogic('product')

上述代码中，useCommonStoreLogic 函数定义了通用的 store 逻辑，并在 useUserStore 和 useProductStore 中复用。

6. Pinia 与 Vue Devtools：调试与监控

Pinia 提供了与 Vue Devtools 的集成，可以方便地调试和监控 store 的状态。

安装 Vue Devtools：

如果还没有安装 Vue Devtools，可以在 Chrome 或 Firefox 浏览器中安装。

使用 Vue Devtools：

在 Vue Devtools 中，可以查看 store 的 state、getters 和 actions。还可以查看 state 的变化历史，并回滚到之前的状态。

时间旅行调试：

Vue Devtools 提供了时间旅行调试功能，可以逐步回放 state 的变化过程，以便更好地理解应用程序的行为。

7. 实战案例：构建一个简单的 Todo 应用

下面，我们通过一个实战案例来演示如何使用 Pinia 构建一个简单的 Todo 应用。

定义 TodoStore：

// store/todo.ts
import { defineStore } from 'pinia'

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

export const useTodoStore = defineStore('todo', {
  state: () => ({
    todos: [] as Todo[]
  }),
  getters: {
    completedTodos: (state) => state.todos.filter(todo => todo.completed),
    pendingTodos: (state) => state.todos.filter(todo => !todo.completed)
  },
  actions: {
    addTodo(text: string) {
      const id = Math.random()
      this.todos.push({ id:id, text, completed: false })
    },
    toggleTodo(id: number) {
      const todo = this.todos.find(todo => todo.id === id)
      if (todo) {
        todo.completed = !todo.completed
      }
    },
    deleteTodo(id: number) {
      this.todos = this.todos.filter(todo => todo.id !== id)
    }
  }
})

组件中使用 TodoStore：



Todo List




{{ todo.text }}
Delete


Completed Todos: {{ todoStore.completedTodos.length }}
Pending Todos: {{ todoStore.pendingTodos.length }}




import { useTodoStore } from './stores/todo'
import { ref } from 'vue'

const todoStore = useTodoStore()
const newTodoText = ref('')

const addTodo = () => {
  if (newTodoText.value.trim()) {
    todoStore.addTodo(newTodoText.value.trim())
    newTodoText.value = ''
  }
}

const toggleTodo = (id: number) => {
  todoStore.toggleTodo(id)
}

const deleteTodo = (id: number) => {
  todoStore.deleteTodo(id)
}



.completed {
  text-decoration: line-through;
  color: gray;
}


8. 常见问题与最佳实践

避免在 state 中存储大型对象或数组。 尽量将大型数据拆分成多个小的 state，或者使用 Vue 的 reactive 函数将大型数据转换为响应式对象。

使用 TypeScript 定义 state 的类型。 这可以提高代码的可读性和可维护性。

合理使用 getters。 getters 可以缓存计算结果，避免重复计算。

将 store 的逻辑提取到函数中，以便复用。

使用 Vue Devtools 调试和监控 store 的状态。

9. 快速回顾与重点强调

Pinia 的 store 通过 defineStore 定义，包含 state、getters 和 actions 三个核心部分。state 存储应用状态，getters 派生状态，actions 修改状态。合理利用它们可以构建高效、可维护的状态管理方案。