Pinia 是 Vue 的状态管理库,类似于 Vuex,但具有更简单的 API 和更好的性能。它提供了一种更简单、更灵活的方式来管理应用程序的状态。
1. 安装 Pinia
在 Vue 3 项目中安装 Pinia,可以使用 npm 或 yarn:
npm install pinia
# or
yarn add pinia
2. 基本使用
2.1 创建 Pinia 实例
在 main.js 文件中创建 Pinia 实例,并将其传递给 Vue 应用程序:
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");
2.2 创建 Store
在 src 目录下创建一个 stores 目录,并在其中创建一个 store 文件,例如 user.js:
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", {
state: () => ({
name: "",
age: 0,
}),
actions: {
setName(name) {
this.name = name;
},
setAge(age) {
this.age = age;
},
},
getters: {
fullName: (state) => `${state.name} ${state.age}`,
},
});
2.3 使用 Store
在组件中使用 store,可以通过 useStore
函数来获取 store 实例:
import { useUserStore } from "@/stores/user";
const userStore = useUserStore();
userStore.name; // 访问 state
userStore.setName("zhang san"); // 调用 action
userStore.setAge(18); // 调用 action
userStore.fullName; // 访问 getter
3. 示例
3.1 订阅 $onAction
$onAction
用于订阅 store 中的 action 执行。它可以在 action 执行前、执行后或发生错误时进行拦截和处理。
3.1.1 日志记录
// stores/user.js
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", {
state: () => ({
name: "",
age: 0,
}),
actions: {
async updateUserInfo(name, age) {
this.name = name;
this.age = age;
},
},
});
// 组件中
import { useUserStore } from "@/stores/user";
const userStore = useUserStore();
// 订阅所有 action
userStore.$onAction(
({
name, // action 名称
args, // 传递给 action 的参数数组
store, // store 实例
after, // action 执行后的钩子
onError, // action 执行出错时的钩子
}) => {
// action 执行前的逻辑
console.log(`Action ${name} started with args:`, args);
// action 执行后的逻辑
after((result) => {
console.log(`Action ${name} completed with result:`, result);
});
// action 执行出错时的逻辑
onError((error) => {
console.error(`Action ${name} failed with error:`, error);
});
}
);
3.1.2 性能监控
const userStore = useUserStore();
userStore.$onAction(({ name, args, store, after }) => {
const startTime = performance.now();
after(() => {
const endTime = performance.now();
console.log(`Action ${name} took ${endTime - startTime}ms to execute`);
});
});
3.1.3 错误处理和重试机制
// stores/user.js
export const useUserStore = defineStore("user", {
state: () => ({
name: "",
age: 0,
}),
actions: {
async fetchUserData() {
// 模拟 API 调用
const response = await fetch("/api/user");
const data = await response.json();
this.name = data.name;
this.age = data.age;
},
},
});
// 组件中
const userStore = useUserStore();
userStore.$onAction(({ name, args, store, after, onError }) => {
let retries = 0;
const maxRetries = 3;
onError(async (error) => {
if (retries < maxRetries) {
retries++;
console.log(`Retrying action ${name}, attempt ${retries}`);
await store.fetchUserData();
} else {
console.error(`Action ${name} failed after ${maxRetries} retries`);
}
});
});
3.1.4 权限控制
// stores/user.js
export const useUserStore = defineStore("user", {
state: () => ({
name: "",
age: 0,
role: "user",
}),
actions: {
updateUserInfo(name, age) {
this.name = name;
this.age = age;
},
deleteUser() {
// 删除用户的操作
},
},
});
// 在组件中使用
const userStore = useUserStore();
userStore.$onAction(({ name, args, store, after, onError }) => {
// 检查权限
if (name === "deleteUser" && store.role !== "admin") {
onError(new Error("Permission denied"));
return;
}
after(() => {
console.log(`Action ${name} executed successfully`);
});
});
3.1.5 取消订阅
$onAction
的返回值是一个函数,调用该函数可以取消订阅:
const unsubscribe = userStore.$onAction(({ name, after }) => {
after(() => {
console.log(`Action ${name} completed`);
});
});
// 取消订阅
unsubscribe();
3.2 订阅 $subscribe
$subscribe
用于订阅 store 中的 state 变化。它可以在 state 变化时进行拦截和处理。
const userStore = useUserStore();
userStore.$subscribe((mutation, state) => {
// mutation 是一个对象,包含以下属性:
// type: 变化的类型,例如 "direct" 或 "patch"
// storeId: store 的 ID
// payload: 变化的 payload
// 检查是否是 name 属性的变化
if (
mutation.type === "direct" &&
mutation.storeId === "user" &&
mutation.payload.name
) {
// 同步 name 属性到其他地方
}
});
3.3 $patch
$patch
用于批量更新 store 中的 state。它可以在一次操作中更新多个属性,而不需要逐个调用 set
方法。
const userStore = useUserStore();
userStore.$patch({
name: "zhang san",
age: 18,
});
3.4 $reset
$reset
用于重置 store 中的 state 到初始状态。它可以在需要重置 store 中的所有数据时使用。
const userStore = useUserStore();
userStore.$reset();
3.5 持久化 Store
// main.js
import { createPersistedState } from "pinia-plugin-persistedstate";
import { createPinia } from "pinia";
const pinia = createPinia();
pinia.use(createPersistedState());
app.use(pinia);
app.mount("#app");
// user.js
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", {
state: () => ({
firstname: "",
lastname: "",
}),
actions: {
setFirstname(name) {
this.firstname = name;
},
setLastname(name) {
this.lastname = name;
},
},
getters: {
fullName: (state) => `${state.firstname} ${state.lastname}`,
}
},
persist: true, // 启用持久化存储
});
3.6 路由守卫
// main.js
import { createRouter, createWebHistory } from "vue-router";
import { createPinia } from "pinia";
import { useUserStore } from "./stores/user";
const router = createRouter({
history: createWebHistory(),
routes: [
// 路由配置
],
});
router.beforeEach((to, from, next) => {
const userStore = useUserStore();
if (to.meta.requiresAuth && !userStore.isAuthenticated) {
next({ name: "login" });
} else {
next();
}
});
const pinia = createPinia();
pinia.use(({ store }) => {
store.$subscribe((mutation, state) => {
if (mutation.type === "direct" && mutation.storeId === "user") {
// 检查是否需要重新导航
if (mutation.payload.isAuthenticated) {
router.push({ name: "home" });
}
}
});
});
app.use(router);
app.use(pinia);
app.mount("#app");
3.7 热更新
// main.js
import { createApp } from "vue";
import { createPinia, acceptHMRUpdate } from "pinia";
import App from "./App.vue";
const app = createApp(App);
const pinia = createPinia();
pinia.use(({ store }) => {
store.$subscribe((mutation, state) => {
if (mutation.type === "direct" && mutation.storeId === "user") {
// 检查是否需要重新导航
if (mutation.payload.isAuthenticated) {
router.push({ name: "home" });
}
}
});
});
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(app, import.meta.hot));
}
app.use(pinia);
app.mount("#app");
原文链接:https://code.ifrontend.net/archives/323,转载请注明出处。
评论0