在现代前端开发中,状态管理是确保应用程序高效、可维护的关键因素之一。随着 Vue.js 的普及,Pinia 作为其新一代状态管理库,逐渐成为开发者的热门选择。Pinia 的设计不仅简洁明了,还提供了强大的功能,旨在简化状态管理的复杂性。
本文将深入探讨 Pinia 中的一些核心概念,帮助你更好地理解这个框架的工作原理和优势。我们将围绕 Pinia 的基本结构、状态、 getters 和 actions 等关键组成部分进行详细解析。同时,我们还将通过实例演示如何在实际项目中应用这些概念,从而实现高效的状态管理和数据流动。
使用 Pinia 的核心在于定义“状态仓库”Store。在定义 Store 时,我们可以对要使用的状态数据进行定义。Pinia 框架中提供了 defineStore
方法来生成 Store 实例。定义一个 Store 非常简单:
// CounterState.js
const userInfoStore = defineStore('userInfo', (/* ... */));
defineStore
方法有两个参数,第一个参数为 Store 的名称,它需要是唯一的,第二个参数为配置对象或 setup
方法。13.1 节中的示例代码采用了配置对象的方式定义 Store,后续我们将采用 setup
方法的方式来定义 Store,例如:
// counterState.js
import { ref } from 'vue';
export const userInfoStore = defineStore('userInfo', () => {
// setup 方法的用法一致
const name = ref("nick");
const age = ref(15);
function incrementAge() {
age.value += 1;
}
return { name, age, incrementAge };
});
在使用时,只要引入此 Store 对象,接口创建出引用实例,对其内部的状态数据和动作方法可以直接调用:
<!-- HelloWorld.vue -->
<script setup>
import { userInfoStore } from '../CounterState';
const userInfo = userInfoStore();
</script>
<template>
<h1 @click="userInfo.incrementAge">
name: {{ userInfo.name }}, age: {{ userInfo.age }}
</h1>
</template>
State 指的是状态数据。在 Pinia 中,支持通过选项式 API 或组合式 API 来定义状态。如果选择使用选项式 API 进行定义,在配置对象中设置 state
选项即可。此选项是一个函数,其返回所需的状态数据。值得注意的是,使用选项式 API 返回的状态数据无须通过 ref
等方法包装,它默认具备响应式特性。而使用组合式 API 则更为直接,类似于编写 Vue 组件的过程,在 setup
方法中声明并返回所需的状态数据。
对于在 Store 中定义的状态,我们可以直接访问,包括读取和修改。但需注意,Pinia 不允许在 Store 使用过程中动态添加新的状态。所有需要的状态数据必须在定义 Store 时就明确指定。除直接访问状态数据进行修改外,还可以使用 $patch
方法来批量更新多个状态。例如:
<!-- HelloWorld.vue -->
<script setup>
import { userInfoStore } from '../CounterState';
const userInfo = userInfoStore();
</script>
<template>
<h1 @click="userInfo.incrementAge">
name: {{ userInfo.name }}, age: {{ userInfo.age }}
</h1>
<button @click="userInfo.$patch({ name: 'Jaki', age: 30 })">更改用户信息</button>
</template>
在上面的例子中,调用 patch 方法传入了一个对象,将要修改的状态数据在此对象中指定即可。 patch 方法支持通过一个函数来修改当前状态数据。如果需要订阅 Store 中状态数据的变化来实现某些业务逻辑,可以使用
<!-- HelloWorld.vue -->
<script setup>
import { userInfoStore } from '../CounterState';
const userInfo = userInfoStore();
userInfo.$subscribe((mutation, state) => {
console.log(mutation); // 当前的状态
console.log(state);
});
</script>
无论是直接访问还是使用 patch 方法产生的状态变更,都会执行 subscribe 注册的订阅回调,此回调中的 mutation 参数封装了当次变更的信息,包括的字段如表 13-1 所示。
表 13-1 mutation 参数包括的字段
patch 函数修改storeIdStore 的名字payload$patch
方法所携带的数据
与 Vue 组件中定义计算属性的方式类似,Pinia 中的 Store 也支持定义“计算状态”。在 Vue 中,计算属性通常体现为 Getter 方法,它让我们能够对数据进行处理后再使用。对于 Pinia 而言,提供的状态数据可能并不总是直接适用于页面渲染,例如某些数值数据在展示时可能需要添加单位。通常,这些计算过程是通用的,即需要被多个组件共享,若每个使用该状态数据的组件都重复实现相同的计算逻辑,则会显得冗余。Pinia 允许我们在定义 Store 时,为其添加特定的计算属性,即 Getter 方法,从而避免代码重复,提升维护性和效率。
以组合式 API 为例,使用 Vue 中的 computed
函数来定义“计算状态”,代码如下:
// CounterState.js
import { ref, computed } from 'vue';
const userInfoStore = defineStore('userInfo', () => {
const name = ref("nick");
const age = ref(15);
function incrementAge() {
age.value += 1;
}
const ageString = computed(() => {
return `${age.value}岁`;
});
return { name, age, incrementAge, ageString };
});
export { userInfoStore };
Store 中的计算状态的使用与正常状态数据完全一致。
在 Pinia 中,另一个至关重要的概念是 Action。在 Store 中,我们可以定义一系列操作函数,这些函数通常封装了对状态数据的修改行为。这样做允许我们将复杂的状态变更逻辑集中在 Store 内部,从而提升程序的可扩展性和可维护性。Pinia 中的 Action 具备一项强大功能,即支持订阅。通过对 Store 中的 Action 进行订阅,我们能够监听方法的调用情况以及调用结果,这为状态管理的精细化控制提供了极大的便利。例如:
<!-- HelloWorld.vue -->
<script setup>
import { userInfoStore } from '../CounterState';
const userInfo = userInfoStore();
// 添加 Action 订阅
userInfo.$onAction((action) => {
// 动作的名称
console.log(action.name);
// 当前 Store 实例
console.log(action.store);
// 方法执行的参数
console.log(action.args);
// 方法成功完整执行后的回调钩子
let afterCallback = action.after;
// 方法有异常抛出的回调钩子
let errorCallback = action.onError;
console.log("方法执行开始前..");
// 注册完成的回调
afterCallback(() => {
console.log("方法执行完成");
});
// 注册异常回调
errorCallback(() => {
console.log("方法执行异常");
});
});
</script>
<template>
<h1 @click="userInfo.incrementAge">
name: {{ userInfo.name }}, age: {{ userInfo.age }}, ageString: {{ userInfo.ageString }}
</h1>
</template>
使用 $onAction
添加行为的订阅时,其注册的回调函数的 action
参数中包含如表 13-2 所示的数据。
表 13-2 action 参数中包含的数据
字段名 | 描述 |
---|---|
name | 当前所调用的 action 的名字 |
store | 当前的 Store 实例对象 |
args | 参数列表 |
after | 一个用来注册 Action 成功执行回调的函数 |
onError | 一个用来注册 Action 出现异常回调的函数 |