手把手教你用Pinia实现状态管理:实战案例分享

手把手教你用 Pinia 实现状态管理:实战案例分享

引言

在现代 Web 开发中,状态管理是一个至关重要的环节。随着应用程序的复杂度不断增加,有效地管理和共享组件之间的状态变得越来越具有挑战性。Vue.js 生态系统中,Vuex 长期以来一直是状态管理的首选方案。然而,随着 Vue 3 的推出以及 Composition API 的普及,一个新的状态管理库 Pinia 逐渐崭露头角,并以其简洁性、类型安全性和出色的开发体验赢得了开发者的青睐。

本文旨在深入探讨 Pinia 的核心概念、使用方法,并通过一个实战案例,详细展示如何利用 Pinia 构建一个健壮、可维护的状态管理系统。无需担心先验知识,将从零开始,逐步引导完成整个过程。

Pinia:下一代状态管理方案

为什么选择 Pinia?

在深入了解 Pinia 的具体用法之前,有必要先了解一下它相对于 Vuex 的优势,以及为什么它能成为新一代状态管理方案的有力竞争者。

  1. 更简洁的 API: Pinia 的 API 设计非常简洁直观。摒弃了 Vuex 中繁琐的 mutations 和 actions 的概念,直接通过修改 state 和定义 methods 来操作状态。

  2. 类型安全: Pinia 与 TypeScript 完美集成,提供了完整的类型推断和类型检查。可以有效避免因类型错误导致的运行时问题。

  3. 模块化设计: Pinia 的 store 是完全模块化的。可以轻松地将应用程序的状态分割成多个独立的 store,每个 store 负责管理特定模块的状态。

  4. 与 Composition API 无缝集成:Pinia 的设计理念与 Composition API 高度契合。可以轻松地在 setup 函数中使用 store,无需像 Vuex 那样进行繁琐的映射操作。

  5. Devtools 支持: Pinia 提供了出色的 Devtools 支持。可以方便地查看状态的变化、调试 actions,以及进行时间旅行调试。

  6. 轻量级: Pinia 的体积非常小巧,对应用程序的性能影响微乎其微。

Pinia vs. Vuex:核心概念对比

为了更直观地理解 Pinia 和 Vuex 的差异,将核心概念进行对比分析:

Vuex:

  • State(状态): 单一状态树,应用的所有状态都集中存储在这里。
  • Getters(获取器): 从 state 派生出新的状态,类似于计算属性。
  • Mutations(变更): 唯一允许修改 state 的方式,必须是同步函数。
  • Actions(动作): 可以包含异步操作,通过提交 mutations 来修改 state。
  • Modules(模块): 将 store 分割成多个模块,每个模块拥有自己的 state、getters、mutations 和 actions。

Pinia:

  • State(状态): 与 Vuex 类似,存储应用的状态。但 Pinia 支持多个 store,每个 store 都是独立的。
  • Getters(获取器): 与 Vuex 类似,从 state 派生出新的状态。
  • Actions(动作): Pinia 中没有 mutations 的概念,actions 可以直接修改 state,并且可以包含异步操作。
  • Store(存储): Pinia 的核心概念,类似于 Vuex 的 modules,但更加简洁灵活。

直观呈现差异

状态修改:

  • Vuex: store.commit('mutationName', payload) (通过 mutations)
  • Pinia: store.stateProperty = newValue (直接修改)

异步操作:

  • Vuex: 在 actions 中进行异步操作,然后提交 mutations。
  • Pinia: 在 actions 中直接进行异步操作,并直接修改 state。

模块化:

  • Vuex: 使用 modules 选项将 store 分割成多个模块。
  • Pinia: 每个 store 都是独立的模块,无需额外配置。

通过对比,可以清晰地看到 Pinia 在 API 设计上的简化,以及对异步操作的更自然的处理方式。

Pinia 基础入门

安装

使用 npm 或 yarn 安装 Pinia:

```bash
npm install pinia

或者

yarn add pinia
```

创建 Store

src 目录下创建一个 stores 文件夹(命名非强制),然后在其中创建一个文件,例如 counter.js

```javascript
// src/stores/counter.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
async incrementAsync() {
await new Promise((resolve) => setTimeout(resolve,1000))
this.count++
}
},
});
```

  • defineStore: Pinia 提供的函数,用于定义一个 store。第一个参数是 store 的唯一 ID,第二个参数是 store 的配置对象。
  • state: 一个返回初始 state 的函数。
  • getters: 定义计算属性,可以访问 state。
  • actions: 定义修改 state 的方法,可以包含异步操作。

在组件中使用 Store

在 Vue 组件中,可以通过 useCounterStore 访问并使用 store:

```vue

```

  • useCounterStore(): 获取 store 实例。
  • storeToRefs: Pinia提供的工具函数,用于从 store 中解构出 state 和 getters,并保持它们的响应性。
  • 可以直接解构actions,因为actions本身只是一个函数

实战案例:构建一个任务管理应用

将通过一个简单的任务管理应用,来演示如何使用 Pinia 进行状态管理。

需求分析

应用需要具备以下功能:

  1. 显示任务列表。
  2. 添加新任务。
  3. 标记任务为已完成/未完成。
  4. 删除任务。
  5. 过滤任务(显示全部、已完成、未完成)。

创建 Store

src/stores 目录下创建一个 todos.js 文件:

```javascript
// src/stores/todos.js
import { defineStore } from 'pinia';

export const useTodosStore = defineStore('todos', {
state: () => ({
todos: [],
filter: 'all', // 'all', 'completed', 'active'
}),
getters: {
filteredTodos: (state) => {
if (state.filter === 'completed') {
return state.todos.filter((todo) => todo.completed);
} if (state.filter === 'active') {
return state.todos.filter((todo) => !todo.completed);
}
return state.todos;
},
},
actions: {
addTodo(text) {
this.todos.push({
id: Date.now(),
text,
completed: false,
});
},
toggleTodo(id) {
const todo = this.todos.find((todo) => todo.id === id);
if (todo) {
todo.completed = !todo.completed;
}
},
deleteTodo(id) {
this.todos = this.todos.filter((todo) => todo.id !== id);
},
setFilter(filter) {
this.filter = filter;
},
},
});
```

创建组件

TaskList.vue

```vue

```

AddTask.vue

```vue

```

FilterTasks.vue

```vue

```

App.vue (组合组件)

```vue

```

项目结构

src/
├── components/
│ ├── AddTask.vue
│ ├── TaskList.vue
│ └── FilterTasks.vue
├── stores/
│ └── todos.js
└── App.vue

运行效果

完成了以上步骤,就构建了一个完整的任务管理应用。通过这个应用,可以添加、删除、标记任务,并根据不同的状态进行过滤。整个应用的状态都由 Pinia 进行管理,组件之间通过 store 进行通信,实现了数据共享和状态同步。

进阶技巧与最佳实践

持久化存储

默认情况下,Pinia store 中的数据是存储在内存中的,页面刷新后数据会丢失。如果需要持久化存储,可以使用 pinia-plugin-persistedstate 插件。

  1. 安装插件:

bash
npm install pinia-plugin-persistedstate

  1. main.js 中引入并使用插件:

```javascript
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

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

  1. 在 store 中配置持久化选项:

javascript
// src/stores/todos.js
export const useTodosStore = defineStore('todos', {
// ...
persist: true, //开启持久化
//也可以自定义存储位置,和存储的key值
/*
persist: {
storage: sessionStorage, // 默认是 localStorage
paths: ['todos'], //指定要存储的state,不指定则存储全部
},
*/
});

这样,useTodosStore中的数据就会被自动的持久化了

Store 之间的交互

在复杂的应用中,不同的 store 之间可能需要进行交互。Pinia 允许在 store 的 actions 中访问其他 store。

```javascript
// src/stores/user.js
import { defineStore } from 'pinia';
import { useTodosStore } from './todos';

export const useUserStore = defineStore('user', {
state: () => ({
name: 'John Doe',
}),
actions: {
async loadUserData() {
// 假设从 API 获取用户信息
const userData = await fetch('/api/user').then((res) => res.json());
this.name = userData.name;

  // 访问 todos store
  const todosStore = useTodosStore();
  todosStore.addTodo(`Welcome, ${this.name}!`);
},

},
});
```

组合式 API 中的 Store

在 Composition API 中,可以使用 computedwatch 来监听 store 中的状态变化。

```vue

```

结语:Pinia 的未来

Pinia 以其简洁性、类型安全性和出色的开发体验,正在迅速成为 Vue 生态系统中状态管理的新标准。虽然本文只介绍了 Pinia 的基础用法和一些进阶技巧,但足以应对大多数应用场景。随着学习的深入,还会发现 Pinia 更多强大的功能和灵活的用法。Pinia 的出现,为构建大型、复杂的 Vue 应用提供了更强有力的支持。

THE END