主题
Pinia 状态管理与持久化
快速开始
js
//main.js
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate); // 使用持久化插件
app.use(pinia);store
定义方式
js
// stores/modules/toc.store.js
import { defineStore } from 'pinia';
export const useTocStore = defineStore('toc', {
state: () => ({
tocList: [],
isVisible: true,
currentRoutePath: '',
}),
getters: {
shouldShowToc() {
return this.tocList.length > 0 || this.isLoading;
},
},
actions: {
addTocItem(item) {
if (!this.tocList.find((i) => i.id === item.id)) {
this.tocList.push(item);
}
},
clearToc() {
this.tocList = [];
},
},
// 持久化配置 - 4.3.0版本的最新配置方式
persist: [
{
key: 'toc-store',
storage: localStorage,
pick: ['isVisible'], // 只持久化特定字段
},
],
});js
// stores/modules/theme.store.js
import { defineStore } from 'pinia';
import { reactive } from 'vue';
export const useThemeStore = defineStore(
'theme',
() => {
// 状态定义
const themeConfig = reactive({
layout: 'centerLayout',
theme: 'redTheme',
fontSize: 'normal',
});
// actions
const updateLayout = (layout) => {
themeConfig.layout = layout;
};
const updateTheme = (theme) => {
themeConfig.theme = theme;
// 更新DOM中的主题类
const body = document.body;
if (theme === 'blueTheme') {
body.classList.add('theme-blue');
body.classList.remove('theme-default');
} else {
body.classList.add('theme-default');
body.classList.remove('theme-blue');
}
};
return {
themeConfig,
updateLayout,
updateTheme,
};
},
{
// 持久化配置
persist: {
key: 'vue-vite-theme',
storage: localStorage,
},
}
);使用方法
一、在组件中使用
vue
<template>
<div>
<p>当前主题: {{ themeStore.themeConfig.theme }}</p>
<button @click="changeTheme">切换主题</button>
</div>
</template>
<script setup>
import { useThemeStore } from '@/stores/modules/theme.store';
const themeStore = useThemeStore();
function changeTheme() {
const newTheme = themeStore.themeConfig.theme === 'redTheme' ? 'blueTheme' : 'redTheme';
themeStore.updateTheme(newTheme);
}
</script>二、解构
使用
storeToRefs可以保持响应性的同时解构 store 属性
vue
<script setup>
import { useThemeStore } from '@/stores/modules/theme.store';
import { storeToRefs } from 'pinia';
const themeStore = useThemeStore();
// 使用 storeToRefs 保持响应性
const { themeConfig } = storeToRefs(themeStore);
// actions 可以直接解构
const { updateTheme } = themeStore;
function changeTheme() {
const newTheme = themeConfig.value.theme === 'redTheme' ? 'blueTheme' : 'redTheme';
updateTheme(newTheme);
}
</script>三、相互调用
js
import { defineStore } from 'pinia';
import { useThemeStore } from './theme.store';
export const useUserStore = defineStore('user', {
state: () => ({
preferences: {
theme: 'default',
},
}),
actions: {
// 在一个 store 中调用另一个 store 的 action
applyUserPreferences() {
const themeStore = useThemeStore();
themeStore.updateTheme(this.preferences.theme);
},
},
});持久化
配置
一、安装
shell
npm i pinia-plugin-persistedstate二、配置
js
export const useCounterStore = defineStore("counter", {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++;
}
},
persist: true // 最简单的配置,持久化所有状态
});js
export const useUserStore = defineStore(
"user",
() => {
const user = ref(null);
const setUser = newUser => {
user.value = newUser;
};
return { user, setUser };
},
{
persist: true // 最简单的配置,持久化所有状态
}
);自定义持久化字段
警告
在 pinia-plugin-persistedstate 4.3.0 版本中,选择要持久化的字段应使用 pick 而不是 paths。 使用错误的属性名可能导致整个 state 被持久化,而不是只持久化指定字段。
js
export const useAdvancedStore = defineStore("advanced", {
state: () => ({
user: null,
token: null,
settings: {
theme: "light",
notifications: true
},
tempData: [] // 临时数据,不需要持久化
}),
persist: {
// 自定义存储键名
key: "my-custom-key",
// 使用 sessionStorage 而非默认的 localStorage
storage: sessionStorage,
// 只持久化特定字段的数据(4.3.0版本使用pick而不是paths)
pick: ["user", "token", "settings.theme"],
// 自定义序列化/反序列化
serializer: {
deserialize: JSON.parse,
serialize: JSON.stringify
},
// 持久化前的数据转换
beforeRestore: context => {
console.log("即将恢复数据", context);
},
// 持久化后的回调
afterRestore: context => {
console.log("数据已恢复", context);
}
}
});多策略
可以为不同的数据配置不同的持久化策略,多策略配置使用数组形式直接定义,而不是通过
strategies属性
js
export const useMultiStore = defineStore("multi", {
state: () => ({
user: null,
token: null,
preferences: {
theme: "light",
fontSize: "medium"
},
cachedData: {}
}),
// 4.3.0版本使用数组形式定义多策略
persist: [
// 敏感数据使用 sessionStorage,关闭浏览器后清除
{
key: "auth-data",
storage: sessionStorage,
pick: ["token", "user"]
},
// 用户偏好使用 localStorage 长期保存
{
key: "user-preferences",
storage: localStorage,
pick: ["preferences"]
},
// 缓存数据使用自定义存储
{
key: "cached-data",
storage: {
getItem: key => {
// 从 IndexedDB 或其他存储获取数据
return localStorage.getItem(key);
},
setItem: (key, value) => {
// 存储到 IndexedDB 或其他存储
localStorage.setItem(key, value);
}
},
pick: ["cachedData"]
}
]
});模块化
js
// stores/index.js
const modules = import.meta.glob('./modules/*.store.js', { eager: true });
// 将所有导出的 useXXXStore 统一导出
const stores = {};
for (const path in modules) {
const mod = modules[path];
for (const key in mod) {
stores[key] = mod[key];
}
}
export default stores;实践
重置
javascript
// 在组件中重置 store 状态
const userStore = useUserStore();
function logout() {
userStore.$reset(); // 重置为初始状态
router.push('/login');
}批量更新
javascript
const userStore = useUserStore();
// 批量更新多个状态
userStore.$patch({
name: 'John',
age: 30,
preferences: {
theme: 'dark',
},
});
// 或者使用函数形式进行更复杂的更新
userStore.$patch((state) => {
state.name = 'John';
state.age = 30;
state.lastLogin = new Date();
state.loginCount++;
});订阅状态变化
javascript
const userStore = useUserStore();
// 订阅整个 store 的变化
const unsubscribe = userStore.$subscribe(
(mutation, state) => {
// 每次状态变化时触发
console.log('状态变化:', mutation);
console.log('当前状态:', state);
// 可以在这里执行额外的操作,如同步到服务器
saveToLocalStorage('user-state', state);
},
{ detached: true }
); // detached: true 表示组件卸载后依然保持订阅
// 在需要时取消订阅
onUnmounted(() => {
unsubscribe();
});