前置知识: Vue 3

Pinia 状态管理详解

00:00
5 min Intermediate 2026/5/3

Store 定义、状态读写、异步操作与插件机制。

1. Pinia 概述

Pinia 是 Vue 3 官方推荐的状态管理库,它是 Vuex 的替代品,提供了更简洁的 API 和更好的 TypeScript 支持。

1.1 主要特性

  • 简洁的 API:使用组合式 API 风格
  • 更好的 TypeScript 支持:无需手动类型声明
  • 模块化设计:支持多个 Store
  • 支持插件:可以扩展 Pinia 功能
  • 支持持久化:可以轻松实现状态持久化
  • 支持热更新:开发时可以热更新状态
  • 支持 SSR:服务端渲染友好

2. 环境搭建

2.1 安装 Pinia

 # 使用 npm
 npm install pinia
 # 使用 yarn
 yarn add pinia

2.2 基本配置

// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount('#app');

3. 基础用法

3.1 创建 Store

 // store/counter.ts
 import { defineStore } from 'pinia'
 export const useCounterStore = defineStore('counter', {
  // 状态
  state: () => ({
  count: 0,
  name: '计数器'
  }),
  // 计算属性
  getters: {
  doubleCount: (state) => state.count * 2,
  // 可以访问其他 getter
  doubleCountPlusOne: (state, getters) => getters.doubleCount + 1
  },
  // 方法
  actions: {
  increment() {
  this.count++
  },
  incrementBy(amount: number) {
  this.count += amount
  },
  // 异步操作
  async incrementAsync() {
  await new Promise(resolve => setTimeout(resolve, 1000))
  this.count++
  }
  }
 }

3.2 使用 Store

<template>
  <div>
    <h1>{{ counterStore.name }}</h1>
    <p>Count: {{ counterStore.count }}</p>
    <p>Double Count: {{ counterStore.doubleCount }}</p>
    <p>Double Count Plus One: {{ counterStore.doubleCountPlusOne }}</p>
    <button @click="counterStore.increment">Increment</button>
    <button @click="counterStore.incrementBy(5)">Increment by 5</button>
    <button @click="counterStore.incrementAsync">Increment Async</button>
  </div>
</template>
<script setup lang="ts">
import { useCounterStore } from '../store/counter';
const counterStore = useCounterStore();
</script>

4. 状态管理

4.1 直接修改状态

<script setup lang="ts">
import { useCounterStore } from '../store/counter';
const counterStore = useCounterStore();
// 直接修改状态
counterStore.count = 10;
</script>

4.2 使用 $patch 批量修改

<script setup lang="ts">
import { useCounterStore } from '../store/counter'
const counterStore = useCounterStore()
// 批量修改状态
counterStore.$patch({
 count: 20,
 name: '新计数器'
}
// 使用函数形式批量修改
counterStore.$patch((state) => {
 state.count += 10
 state.name = '更新后的计数器'
}
</script>

4.3 重置状态

<script setup lang="ts">
import { useCounterStore } from '../store/counter';
const counterStore = useCounterStore();
// 重置状态到初始值
function resetStore() {
  counterStore.$reset();
}
</script>

5. Getters

5.1 基础 Getters

 export const useCounterStore = defineStore('counter', {
  state: () => ({
  count: 0
  }),
  getters: {
  // 基础 getter
  doubleCount: (state) => state.count * 2,
  // 带参数的 getter
  getCountBy: (state) => (multiplier: number) => state.count * multiplier
  }
 }
<template>
  <div>
    <p>Double Count: {{ counterStore.doubleCount }}</p>
    <p>Count * 3: {{ counterStore.getCountBy(3) }}</p>
  </div>
</template>

5.2 访问其他 Store 的 Getters

 // store/user.ts
 import { defineStore } from 'pinia'
 import { useCounterStore } from './counter'
 export const useUserStore = defineStore('user', {
  state: () => ({
  name: '张三'
  }),
  getters: {
  // 访问其他 store 的 getter
  userWithCount: (state) => {
  const counterStore = useCounterStore()
  return `${state.name} 的计数器值为 ${counterStore.count}`
  }
  }
 }

6. Actions

6.1 基础 Actions

 export const useCounterStore = defineStore('counter', {
  state: () => ({
  count: 0
  }),
  actions: {
  increment() {
  this.count++
  },
  incrementBy(amount: number) {
  this.count += amount
  }
  }
 }

6.2 异步 Actions

 export const useUserStore = defineStore('user', {
  state: () => ({
  userList: [],
  loading: false
  }),
  actions: {
  async fetchUsers() {
  this.loading =
  try {
  const response = await fetch('https://api.example.com/users')
  this.userList = await response.json()
  } catch (error) {
  console.error('获取用户列表失败:', error)
  } finally {
  this.loading = false
  }
  }
  }
 }

6.3 访问其他 Store 的 Actions

 // store/cart.ts
 import { defineStore } from 'pinia'
 import { useUserStore } from './user'
 export const useCartStore = defineStore('cart', {
  state: () => ({
  items: []
  }),
  actions: {
  addItem(item: any) {
  this.items.push(item)
  // 访问其他 store 的 action
  const userStore = useUserStore()
  userStore.updateLastActivity()
  }
  }
 }

7. 模块化

7.1 基本模块化

// store/modules/user.ts
export const useUserStore = defineStore('user', {
  // ...
  ,
});
// store/modules/cart.ts
export const useCartStore = defineStore('cart', {
  // ...
  ,
});
// store/index.ts
export * from './modules/user';
export * from './modules/cart';

7.2 组合式 Store

// store/user.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useUserStore = defineStore('user', () => {
  // 状态
  const name = ref('张三');
  const age = ref(20);
  // 计算属性
  const isAdult = computed(() => age.value >= 18);
  // 方法
  function updateName(newName: string) {
    name.value = newName;
  }
  function incrementAge() {
    age.value++;
  }
  return {
    name,
    age,
    isAdult,
    updateName,
    incrementAge,
  };
});

8. 持久化

8.1 使用 pinia-plugin-persistedstate

安装:

 npm install pinia-plugin-persistedstate

配置:

// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import App from './App.vue';
const app = createApp(App);
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
app.mount('#app');

使用:

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  // 启用持久化
  persist: True,
});

8.2 自定义持久化配置

 export const useUserStore = defineStore('user', {
  state: () => ({
  name: '张三',
  age: 20,
  token: ''
  }),
  persist: {
  // 存储到 localStorage
  storage: localStorage,
  // 只持久化特定字段
  paths: ['name', 'token'],
  // 自定义键名
  key: 'user-storage'
  }
 }

9. 插件

9.1 自定义插件

// pinia plugins
import { PiniaPluginContext } from 'pinia';
export function myPiniaPlugin(context: PiniaPluginContext) {
  const { store } = context;
  // 在 store 初始化时执行
  console.log('Store initialized:', store.$id);
  // 添加自定义方法
  store.$resetState = () => {
    store.$reset();
    console.log('Store reset:', store.$id);
  };
  // 监听状态变化
  store.$subscribe((mutation, state) => {
    console.log('State changed:', mutation.type, state);
  });
}
// main.ts
import { createPinia } from 'pinia';
import { myPiniaPlugin } from './plugins/pinia';
const pinia = createPinia();
pinia.use(myPiniaPlugin);

9.2 使用官方插件

  • pinia-plugin-persistedstate:状态持久化
  • pinia-plugin-debug:调试工具
  • pinia-plugin-logger:日志记录

10. 类型安全

10.1 TypeScript 支持

 // store/user.ts
 import { defineStore } from 'pinia'
 interface User {
  id: number
  name: string
  email: string
 }
 export const useUserStore = defineStore('user', {
  state: (): {
  users: User[]
  loading: boolean
  } => ({
  users: [],
  loading: false
  }),
  getters: {
  activeUsers: (state): User[] => {
  return state.users.filter(user => user.name.length > 0)
  }
  },
  actions: {
  addUser(user: User) {
  this.users.push(user)
  }
  }
 }

10.2 组合式 Store 的类型

// store/user.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
interface User {
  id: number;
  name: string;
  ;
}
export const useUserStore = defineStore('user', () => {
  const users = ref<User[]>([]);
  const loading = ref(false);
  const activeUsers = computed(() => {
    return users.value.filter((user) => user.name.length > 0);
  });
  function addUser(user: User) {
    users.value.push(user);
  }
  return {
    users,
    loading,
    activeUsers,
    addUser,
  };
  ;
});

11. 最佳实践

  1. 使用模块化:将不同功能的状态分离到不同的 Store 中
  2. 使用组合式 API:对于复杂的 Store,使用组合式 API 风格
  3. 使用 TypeScript:提供类型安全,减少运行时错误
  4. 合理使用持久化:只持久化必要的状态
  5. 使用 actions 处理复杂逻辑:将业务逻辑封装在 actions 中
  6. 使用 getters 处理派生状态:避免在组件中重复计算
  7. 监听状态变化:使用 $subscribe 监听状态变化,执行副作用
  8. 测试 Store:确保 Store 的逻辑正确

12. 常见问题与解决方案

12.1 状态更新后组件不更新

问题:修改状态组件没有重新渲染 解决方案:确保使用正确的方式修改状态对象数组,使用 $patch 或直接替换整个对象/数组

12.2 持久化不生效

问题状态持久化后刷新状态丢失 解决方案检查持久化配置是否正确,确保存储介质(localStorage/sessionStorage)可用

12.3 多个 Store 之间的依赖

问题个 Store 之间存在依赖 解决方案:在 actions 中按需导入其他 Store,避免在模块顶部直接导入

12.4 异步操作的错误处理

问题:异步 actions 中的错误没有被正确处理 解决方案:使用 try/catch 捕获错误,并在组件处理错误状态

13. 总结

Pinia 是 Vue3 生态系统推荐状态管理库,它提供了简洁的 API、更好的 TypeScript 支持和强大的功能。通过本教程的学习,你应该已经掌握了 Pinia概念和使用方法,可以在实际项目中灵活运用。

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式