写后台管理系统,Tabs 多标签页加上数据持久化绝对是绕不开的需求。
以前我写这块的时候,总会踩进一个极其恶心人的坑:单一数据源冲突 。
怎么个坑法呢?就是一边在 Vue 组件里搞个 ref 本地存一份 tabList,一边为了防刷新,又在 Pinia 里也存一份持久化的 tabList。 结果就是:每次点个标签,既要改本地的数组,又要去同步 Pinia。最崩溃的是,一旦按下 F5 刷新,本地数据瞬间重置,两边的数据立刻开始“打架” 🥊,代码越写越像缝合怪,逻辑简直没眼看。
今天就来拔掉这根刺。核心口诀就一句:干掉组件里的本地状态,让 Pinia 出来统管大局(Single Source of Truth)💡。
📦 一、 把烂摊子交给 Pinia:收拢业务逻辑
既然这个标签列表需要防刷新,那它就不该由某个具体的 Vue 组件来私有维护。我们要把标签的增、删逻辑,以及持久化配置,全部扔进 Pinia 里。视图组件以后只管去“无脑”调用,不管具体怎么实现。
来看看 menuStore.js 里的关键代码:
import { defineStore } from "pinia";
export const menuStore = defineStore("menuStore", {
state: () => ({
// 整个系统里唯一的真实数据源
tabList: [ { title: "首页", path: "/" } ]
}),
getters: {
getTabList(state) => state.tabList
},
actions: {
// 增删逻辑全包了,外面不用操心
addTab(tab) {
let noTab = this.tabList.findIndex(t => t.path === tab.path) === -1
if (noTab) this.tabList.push(tab)
},
removeTab(path) {
this.tabList = this.tabList.filter(t => t.path !== path)
}
},
// 顺手把持久化安排上(关浏览器就清空,用 sessionStorage 正好)
persist: {
storage: sessionStorage,
pick: ['tabList'],
}
});
✂️ 二、 Vue 组件大瘦身:做一个无情的 API 调用机器
Pinia 接管了脏活累活,我们就不需要在本地写什么 const tabList = ref([]) 了。整个 Vue 组件的 <script setup> 瞬间清爽,只剩下单纯的数据映射和事件转发。
看下 Tabs.vue 里的核心逻辑,真的巨清爽:
import { computed } from 'vue'
import { menuStore } from "@/store/menu/index.js";
import { onBeforeRouteUpdate, useRoute } from "vue-router";
const route = useRoute()
const mStore = menuStore()
// 1:当前高亮的标签直接跟路由 path 绑定,这才是真正的数据驱动视图!
// 🌟 修复点:将只读 computed 改造成可读写的 computed
const activeTab = computed({
// 读:告诉 el-tabs 当前高亮哪个标签
get() {
return route.path
},
// 写:当用户点击标签时,el-tabs 会把要跳转的 path 传给 val
set(val) {
// 拦截写入操作,直接让 vue-router 帮我们跳转页面!
router.push(val)
}
})
// 2:抛弃本地 ref,靠 computed 实时去 Store 里拿数据
const tabList = computed(() => {
return mStore.getTabList
})
// 3:删标签?直接把锅甩给 Pinia,组件不管具体的过滤逻辑
const handleTabRemove = (path) => {
mStore.removeTab(path)
}
// 4:路由一变,自动把新页面塞进标签栏
onBeforeRouteUpdate((to) => {
mStore.addTab({
title: to.meta.title,
path: to.path,
})
})
(注:模板层里,<el-tab-pane> 的关闭事件直接绑 handleTabRemove,当前激活项绑 activeTab 就完事了)
🎉 三、 踩坑总结
解决前端这些绕来绕去的交互,关键真不在于你用了多高深的 API,而在于理清数据的归属权 📌。