本篇通过 Pinia 实现侧边栏(Sidebar)的展开收起功能,并通过 Pinia 实现展开状态的持久化。
1 安装 Pinia Persistedstate
Pinia 是 Vue.js 的状态管理库,而 pinia-plugin-persistedstate 是一个针对 Pinia 的插件,它能让 Pinia 管理的状态实现持久化存储。在前端开发中,持久化状态意味着即使页面刷新或用户关闭浏览器重新打开,某些关键状态依然能保持不变,极大提升用户体验。比如侧边栏的展开或收起状态,用户设置后希望再次访问页面时依然保持之前的设置。使用以下命令进行安装:
pnpm install pinia-plugin-persistedstate
2 在 main.ts 中使用
在 main.ts 中引入持久化插件 pinia-plugin-persistedstate,通过pinia.use(piniaPluginPersistedstate) 这行代码,将该插件应用到 Pinia 实例中。这样,后续定义的 Store 中的状态,只要按照插件规则配置,就能实现持久化存储。代码如下:
//main.ts
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import "normalize.css/normalize.css";
import { createPinia } from "pinia";
import element from "./plugins/element";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// import ElementPlus from "element-plus";
// import "element-plus/dist/index.css";
import "@/style/index.scss";
import "uno.css";
const app = createApp(App);
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate); // 安装持久化插件
app.use(router);
app.use(pinia);
app.use(element);
// app.use(ElementPlus);
app.mount("#app");
3 配置 Pinia 和持久化插件
在 src 目录下创建 stores 文件夹,并在其中创建 app.ts 文件,代码如下:
//src/stores/app.ts// 使用defineStore定义名为'app'的store
export const useAppStore = defineStore("app",() => {//定义响应式状态const state = reactive({sidebar: {opened: true}// ...// theme});//计算属性,方便获取sidebar状态const sidebar = computed(() => state.sidebar);//切换侧边栏展开状态的函数const toggleSidebar = () => {state.sidebar.opened = !state.sidebar.opened;};return { state, sidebar, toggleSidebar };},{//持久化配置persist: {storage: window.localStorage,//使用window.localStorage存储状态pick: ["state.sidebar"]//只持久化state.sidebar这个属性}}
);
注:state 必须导出,否则无法使用
在 Store 的定义中,通过 persist 选项来配置持久化相关参数。storage 指定了存储方式,这里使用window.localStorage,意味着状态会存储在浏览器的本地存储中。pick 数组指定了要持久化的具体状态属性,这里只选择了state.sidebar,即只对侧边栏的展开状态进行持久化。如果有多个状态需要持久化,可以在pick数组中添加更多属性路径。
4 创建 Hamburger 组件
在 src/components 下创建 Hamburger 文件夹,其下创建 index.vue 文件,代码如下:
//src/components/Hamburger/index.vue
<template><div class="hamburger-container"><!-- svg-icon组件,用于显示菜单图标,根据collapse状态添加旋转类名 --><svg-iconicon-name="ant-design:bars-outlined"custom-class="hamburger":class="{ 'rotate-180': collapse }"@click="handleClick"></svg-icon></div>
</template><style scoped lang="scss">
.hamburger-container {@apply leading-[50px] float-left cursor-pointer px-10px hover:(bg-black/5);
}
.hamburger {@apply w-30px h-30px transition-transform duration-300;
}
</style><script lang="ts" setup>
// 定义组件props,接收collapse状态
const { collapse } = defineProps({collapse: {type: Boolean,default: false}
});
// 定义组件事件emit,用于触发toggleCollapse事件
const emit = defineEmits<{ (e: "toggleCollapse"): void }>();
// 点击处理函数,触发toggleCollapse事件
const handleClick = () => {emit("toggleCollapse");
};
</script>
5 创建 Navbar 组件
在 src/layout/components 文件夹下创建 Navbar.vue,代码如下:
//src/layout/components/Navbar.vue
<template><div class="navbar" flex><!-- hamburger组件,绑定toggleCollapse事件和collapse状态 --><hamburger@toggleCollapse="toggleSidebar":collapse="sidebar.opened"></hamburger></div>
</template><style scoped lang="scss">
.navbar {@apply h-[var(--navbar-height)];
}
</style><script lang="ts" setup>
import { useAppStore } from "@/stores/app";
// 获取app store中的toggleSidebar和sidebar状态
// 在解构的时候要考虑值是不是对象,如果非对象解构出来就 丧失响应式了
const { toggleSidebar, sidebar } = useAppStore();
</script>
6 修改 Sidebar 组件
修改 src/layout/components/Sidebar/index.vue,使用 pinia 存储的值,代码如下:
//src/layout/components/Sidebar/index.vue
<template><el-menurouterclass="sidebar-container-menu":default-active="defaultActive":background-color="variables.menuBg":text-color="variables.menuText":active-text-color="variables.menuActiveText":collapse="sidebar.opened"><el-menu-item index="/dashboard"><el-icon><setting /></el-icon><template #title>Navigator</template></el-menu-item></el-menu>
</template><script lang="ts" setup>
import { useAppStore } from "@/stores/app";
import variables from "@/style/variables.module.scss";const route = useRoute();
const { sidebar } = useAppStore();
const defaultActive = computed(() => {return route.path;
});
</script>
<style scoped></style>
7 修改 layout
修改 layout/index.vue,代码如下:
//src/layout/index.vue
<template><div class="app-wrapper"><div class="sidebar-container"><sidebar></sidebar></div><div class="main-container"><div class="header"><!-- 上边包含收缩的导航条 --><navbar></navbar></div><div class="app-main"><router-view></router-view></div></div></div>
</template>
<style lang="scss" scoped>
.app-wrapper {@apply flex w-full h-full;.sidebar-container {// 跨组件设置样式@apply bg-[var(--menu-bg)];:deep(.sidebar-container-menu:not(.el-menu--collapse)) {@apply w-[var(--sidebar-width)];}}.main-container {@apply flex flex-col flex-1;}.header {@apply h-84px;.navbar {@apply h-[var(--navbar-height)] bg-yellow;}.tags-view {@apply h-[var(--tagsview-height)] bg-blue;}}.app-main {@apply bg-cyan;min-height: calc(100vh - var(--tagsview-height) - var(--navbar-height));}
}
</style>
至此,就实现了侧边栏的展开收起及展开收起状态的持久化,页面效果如下:
下一篇将继续探讨菜单组件,敬请期待~