小企鹅乐园
47.12M · 2026-02-09
学会Pinia的Setup Store和Option Store基础写法后,想要真正灵活运用Pinia、应对复杂状态管理场景,就必须掌握它的四大核心实例API——reset、onAction。
这4个API贯穿Pinia开发全流程:reset重置状态、onAction方法执行,覆盖“修改-重置-”三大核心需求。
很多开发者用Pinia只停留在基础写法,忽略了这4个API的强大功能,导致代码冗余、状态管理混乱。本文聚焦四大API,从“用法+案例+避坑”三维度拆解,全程结合Vue3 + TS实操,看完直接套用。
为了让API用法更直观,先定义一个通用的Pinia Store(同时兼容Option Store和Setup Store,后续API用法均基于此实例演示,避免重复):
// src/stores/user.ts(Option Store)
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: 'Pinia',
age: 3,
isLogin: false,
roles: ['user']
}),
getters: {
fullName: (state) => `Mr. ${state.name}`
},
actions: {
setLogin(status: boolean) {
this.isLogin = status
},
async fetchUserInfo() {
// 模拟接口请求
const res = await new Promise(resolve => setTimeout(() => resolve({
name: 'Pinia Pro',
age: 4,
roles: ['user', 'admin']
}), 1000))
this.$patch(res) // 后续会讲$patch用法
}
}
})
// src/stores/user.ts(Setup Store)
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
const name = ref('Pinia')
const age = ref(3)
const isLogin = ref(false)
const roles = ref(['user'])
const fullName = computed(() => `Mr. ${name.value}`)
const setLogin = (status: boolean) => {
isLogin.value = status
}
const fetchUserInfo = async () => {
const res = await new Promise(resolve => setTimeout(() => resolve({
name: 'Pinia Pro',
age: 4,
roles: ['user', 'admin']
}), 1000))
// Setup Store 中$patch用法与Option Store一致
useUserStore().$patch({
name: res.name,
age: res.age,
roles: res.roles
})
}
return {
name, age, isLogin, roles,
fullName,
setLogin, fetchUserInfo
}
})
关键说明:以下四大API的用法,完全适用于两种Store写法,仅Setup Store中操作ref状态时需注意.value,API调用逻辑完全一致,后续不再单独区分。
按“修改状态→重置状态→状态→方法”的逻辑拆解,每个API都包含“核心作用+两种用法+实操案例+避坑点”,确保新手能看懂、会用。
用于批量修改多个状态,替代多次单独修改状态(如this.name = xxx、this.age = xxx),减少代码冗余,同时优化性能(Pinia会合并多次状态更新,减少组件重渲染)。
适用场景:一次性修改多个state属性(如接口请求后,同步更新多个状态)。
核心:传递一个对象,对象中的key对应state的属性名,value对应要修改的值,仅修改对象中包含的属性。
// 1. 组件中使用(两种Store写法通用)
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 批量修改name、age、isLogin三个状态
userStore.$patch({
name: 'Pinia Advanced',
age: 5,
isLogin: true,
// 数组类型也可直接修改(覆盖式)
roles: ['admin']
})
// 2. Option Store 的actions中使用
actions: {
updateUserInfo() {
this.$patch({ // this指向当前Store实例
name: 'Pinia Advanced',
age: 5
})
}
}
核心:传递一个函数,函数接收state作为参数,可在函数内部执行复杂的状态修改(如数组push、splice,对象深层修改),比对象式更灵活。
// 复杂修改案例:给roles数组添加新角色,同时修改age
userStore.$patch((state) => {
state.roles.push('superAdmin') // 数组操作
state.age += 1 // 计算后修改
// 支持深层修改(若state有嵌套对象)
// state.info.address = 'xxx'
})
// 对比:若用对象式,数组只能覆盖,无法直接push
userStore.$patch({
roles: [...userStore.roles, 'superAdmin'] // 需手动解构,繁琐
})
将Store的所有state属性,一键恢复到初始状态(即state选项/ref定义时的初始值),无需手动逐个重置,简化逻辑。
适用场景:退出登录、表单重置、页面刷新时,恢复Store初始状态。
// 组件中使用(两种Store写法通用)
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 一键重置所有状态(name恢复为Pinia,age恢复为3,isLogin恢复为false)
const handleReset = () => {
userStore.$reset()
}
// Option Store 的actions中使用
actions: {
logout() {
this.setLogin(false) // 先执行自定义逻辑
this.$reset() // 再重置状态
}
}
Store中所有state属性的变化(单个/多个属性变化均会触发),可获取变化前、变化后的值,用于执行副作用(如日志记录、本地存储同步)。
适用场景:状态变化后执行额外逻辑(如用户信息变化时,同步缓存到localStorage)。
// 组件中使用(两种Store写法通用)
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 状态变化
const unsubscribe = userStore.$subscribe((mutation, state) => {
// mutation:变化相关信息(核心属性如下)
// mutation.storeId:当前Store的id(如user)
// mutation.type:变化类型(direct:直接修改,patch:$patch修改)
// mutation.payload:变化的内容(对象式$patch为修改对象,函数式为undefined)
// state:变化后的最新状态(完整state对象)
console.log('状态变化了', mutation, state)
// 示例:同步状态到localStorage
localStorage.setItem('userState', JSON.stringify(state))
}, {
// 可选配置项(按需开启)
detached: false, // 默认为false,组件卸载后自动取消;true则组件卸载后仍
deep: true, // 默认为true,深度嵌套对象变化;false则不嵌套对象
immediate: false // 默认为false,初始化时不触发;true则初始化时立即触发一次
})
// 手动取消(如组件卸载时)
onUnmounted(() => {
unsubscribe()
})
// Option Store 的actions中使用(较少见,一般在组件中)
actions: {
initSubscribe() {
this.$subscribe((mutation, state) => {
console.log('状态变化', state)
})
}
}
Store中所有actions方法的执行,可在方法执行前、执行后、执行报错时触发回调,用于拦截方法、日志记录、错误处理。
适用场景:拦截异步方法(如请求前loading、请求后处理)、记录方法执行日志、捕获方法报错。
// 组件中使用(两种Store写法通用)
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 所有actions方法的执行
const unsubscribe = userStore.$onAction(({
name, // 当前执行的actions方法名(如fetchUserInfo、setLogin)
args, // 当前方法的参数数组(如setLogin的参数[true])
after, // 方法执行成功后触发的回调
onError // 方法执行失败(报错)时触发的回调
}) => {
// 1. 方法执行前触发(可做拦截、loading等)
console.log(`方法${name}开始执行,参数:`, args)
const loading = ref(true)
// 2. 方法执行成功后触发(可做后续处理)
after((result) => {
console.log(`方法${name}执行成功,返回值:`, result)
loading.value = false
})
// 3. 方法执行失败时触发(可做错误处理)
onError((error) => {
console.error(`方法${name}执行失败,错误:`, error)
loading.value = false
ElMessage.error('操作失败,请重试')
})
}, {
// 可选配置项
detached: false // 与$subscribe一致,组件卸载后是否继续
})
// 手动取消
onUnmounted(() => {
unsubscribe()
})
// 示例:fetchUserInfo异步方法
userStore.fetchUserInfo() // 会触发$onAction的所有回调
| API | 核心作用 | 适用场景 | 关键注意点 |
|---|---|---|---|
| $patch | 批量修改多个状态 | 接口请求后同步更新状态、一次性修改多属性 | 函数式适配复杂修改,对象式简洁首选 |
| $reset | 一键重置所有状态为初始值 | 退出登录、表单重置、页面刷新 | Setup Store仅重置暴露的状态 |
| $subscribe | 所有state变化 | 状态变化后同步缓存、日志记录 | 注意取消,避免内存泄漏 |
| $onAction | actions方法执行(全生命周期) | 异步请求拦截、错误处理、方法日志 | 仅actions中的方法,不局部函数 |
结合“用户信息管理”场景,联用四大API,实现“修改用户信息→状态变化→缓存到本地→重置状态→方法报错”的完整逻辑,代码可直接复制套用:
<template>
<div class="user-manager">
<h3>用户信息管理</h3>
<p>姓名:{{ userStore.name }}</p>
<p>年龄:{{ userStore.age }}</p>
<p>角色:{{ userStore.roles.join(',') }}</p>
<button @click="updateUser">修改用户信息</button>
<button @click="resetUser">重置用户信息</button>
<button @click="fetchUser">获取用户信息(异步)</button>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { onUnmounted } from 'vue'
import { ElMessage } from 'element-plus'
const userStore = useUserStore()
// 1. 状态变化,同步到localStorage
const unsubscribeSub = userStore.$subscribe((_, state) => {
localStorage.setItem('userState', JSON.stringify(state))
ElMessage.success('用户状态已同步缓存')
})
// 2. actions方法执行,处理loading和错误
const unsubscribeAction = userStore.$onAction(({ name, after, onError }) => {
console.log(`方法${name}开始执行`)
const loading = ElMessage.info({ message: '操作中...', duration: 0 })
after(() => {
loading.close()
ElMessage.success(`方法${name}执行成功`)
})
onError((err) => {
loading.close()
ElMessage.error(`方法${name}执行失败:${err.message}`)
})
})
// 3. 批量修改用户信息($patch)
const updateUser = () => {
userStore.$patch({
name: 'Pinia 实战',
age: 6,
roles: ['user', 'admin', 'superAdmin']
})
}
// 4. 重置用户信息($reset)
const resetUser = () => {
userStore.$reset()
}
// 5. 异步获取用户信息(调用actions,触发$onAction)
const fetchUser = () => {
userStore.fetchUserInfo()
}
// 6. 组件卸载,取消所有
onUnmounted(() => {
unsubscribeSub()
unsubscribeAction()
})
</script>
其实这4个API的用法并不复杂,核心是“贴合场景选择”——批量修改用reset,状态用onAction。结合本文的案例多练几遍,就能灵活运用到实际项目中,彻底吃透Pinia的强大之处~