Pinia通过pinia-plugin-persistedstate插件持久化菜单折叠状态,将isCollapse和openeds存入localStorage,组件中绑定default-openeds与collapse,并在@open/@close事件中动态更新openeds数组。

Pinia 本身不直接处理菜单的“折叠/展开逻辑”,而是提供一个集中、响应式、可持久化的状态容器。多级菜单的折叠状态缓存,关键在于:明确要存什么、存在哪、什么时候读写、以及如何与组件联动。下面分三块讲清楚。
一、明确缓存目标:存哪些状态?
多级菜单通常涉及两类状态:
-
全局折叠开关:比如整个侧边栏是否收起(对应
el-menu的:collapse属性),这个值一般布尔型,存在 Pinia 的 state 里即可; -
各子菜单展开项:即每个多级菜单(
el-sub-menu)当前展开的是哪个,用default-openeds控制,它是个字符串数组(如['/system', '/system/user'])。这个数组必须持久化,否则刷新后所有子菜单自动收起。
二、实现持久化:推荐用 pinia-plugin-persistedstate
手动用 sessionStorage 或 localStorage 存取容易出错(类型丢失、更新不同步、没监听变化)。更稳妥的方式是引入官方推荐插件:
- 安装:
npm install pinia-plugin-persistedstate - 在
main.ts中启用(注意顺序):
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
然后在你的菜单 store 中声明需要持久化的字段:
state: () => ({
isCollapse: false,
openeds: [] as string[], // 存展开的 submenu name 数组
}),
persist: {
key: 'menu-state',
paths: ['isCollapse', 'openeds'],
},
})
三、组件中正确绑定与同步
以 Element Plus 为例,确保三个地方对齐:
-
菜单组件:把
openeds绑定到:default-openeds,把isCollapse绑定到:collapse; -
折叠按钮:点击时只调用
menuStore.toggleCollapse(),不要手动改 DOM 或 class; -
子菜单切换:监听
@open和@close事件,动态更新openeds数组(增删)——注意避免重复添加或遗漏删除。
示例片段(菜单组件内):
:default-openeds="menuStore.openeds"
@open="handleOpen"
@close="handleClose"
>
menuStore.openeds = [...new Set([...menuStore.openeds, key])]
}
const handleClose = (key: string) => {
menuStore.openeds = menuStore.openeds.filter(k => k !== key)
}
四、进阶提示:避免常见坑
实际项目中容易忽略的细节:
- 如果菜单数据异步加载(比如从接口拉取),要在数据就绪后再绑定
default-openeds,否则 Vue 会警告“不能在未定义的数组上操作”; - 路由跳转时,若希望“进入某页面自动展开对应菜单”,需在
onBeforeRouteUpdate中根据to.path手动推入父级路径到openeds; - 使用
persistedstate插件时,默认存localStorage,如需 session 级别(关浏览器清空),可在 persist 配置里加storage: sessionStorage。


















