后台管理系统-4-vue3之pinia实现导航栏按钮控制左侧菜单栏的伸缩
文章目录
- 1 引入pinia
- 1.1 引入并注册(main.js)
- 1.2 stores/index.js(pinia示例)
- 1.2.1 定义一个store
- 1.2.2 组件中使用这个store
- 2 pinia实现菜单收缩功能
- 2.1 stores/index.js(pinia)
- 2.2 侧边栏(CommonAside.vue)
- 2.3 导航栏(CommonHeader.vue)
- 3 状态变更的集中管理
- 3.1 stores/index.js(pinia)
- 3.2 导航栏(CommonHeader.vue)
- 4 附录
- 4.1 computed
- 4.2 参考附录
使用pinia进行集中式状态管理,定义一个isCollapse,通过导航栏的按钮修改其值,菜单栏随着其值的变化而展开或伸缩。
1 引入pinia
Pinia是一个专门为Vue.js应用程序设计的状态管理库。
它由Vue.js团队成员开发,作为Vuex的继任者,旨在提供更简洁、更直观的API,并更好地支持TypeScript和组合式API。
作用:用于多个组件共享数据。
安装npm install pinia。
1.1 引入并注册(main.js)
import { createApp } from 'vue'
import App from './App.vue'
import '@/assets/less/index.less';
import router from './router';import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';const app = createApp(App);import { createPinia } from 'pinia';
const pinia = createPinia();
app.use(pinia);for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component);
}app.use(router);
app.use(ElementPlus);
app.mount('#app');
1.2 stores/index.js(pinia示例)
import { defineStore } from "pinia";
import { ref, computed } from "vue";
function initState(){ }
export const useAllDataStore = defineStore("allData", ()=>{// ref() 相当于state属性// computed() 相当于getters// function() 相当于actionsconst state = ref(initState());return {state}
});
响应式: 使用ref()包装state确保它是响应式的。
1.2.1 定义一个store
1.2.2 组件中使用这个store
2 pinia实现菜单收缩功能
(1)收缩
(2)展开
2.1 stores/index.js(pinia)
定义一个变量isCollapse,初始化为true,用于传递给菜单的collapse属性,控制菜单的折叠。
import { defineStore } from "pinia";
import { ref, computed } from "vue";
function initState(){return {isCollapse:true,}
}
export const useAllDataStore = defineStore("allData", ()=>{// ref() 相当于state属性// computed() 相当于getters// function() 相当于actionsconst state = ref(initState());return {state}
});
注意:useAllDataStore不是默认导出,所以在其他组件中导入时,需要使用{}。
2.2 侧边栏(CommonAside.vue)
(1)菜单的宽度需要变化。
(2)el-menu的折叠属性collapse需要随着isCollapse变化。
(3)collapse-transition,是否开启折叠动画,设置为不开启。
(4)h3显示的字样,使用v-show属性,随着isCollapse变化。
(5)使用计算属性获取值,保持其响应性。
computed会“追踪”store.state.isCollapse的变化。
一旦store.state.isCollapse改变,isCollapse.value会自动更新。
在模板中使用时,会自动重新渲染。
(6)为了让width本身也成为响应式数据,能自动更新,也用computed进行包装。
通过计算属性isCollapse监听store中的isCollapse状态。
通过计算属性width根据折叠状态动态返回宽度值:折叠时64px,展开时180px。
<template><el-aside :width="width"><el-menu background-color="#545c64" :collapse="isCollapse" :collapse-transition="false"><h3 v-show="!isCollapse">后台管理系统</h3><h3 v-show="isCollapse">后台</h3><el-menu-item v-for="item in noChildren" :index="item.path" :key="item.path"><component class="icons" :is="item.icon"></component><span>{{ item.label }}</span></el-menu-item><el-sub-menu v-for="item in hasChildren" :index="item.path" :key="item.path"><template #title><component class="icons" :is="item.icon"></component><span>{{ item.label }}</span></template><el-menu-item-group><el-menu-item v-for="(subItem,subIndex) in item.children" :index="subItem.path" :key="subItem.path"><component class="icons" :is="subItem.icon"></component><span>{{ subItem.label }}</span></el-menu-item></el-menu-item-group></el-sub-menu></el-menu></el-aside>
</template >
<script setup>import {useAllDataStore} from '@/stores'const store = useAllDataStore()const isCollapse = computed(()=>store.state.isCollapse)const width = computed(()=>isCollapse.value?'64px':'180px')import {ref,computed} from 'vue'import { useRouter } from 'vue-router';const router = useRouter()const list = ref([{path:"/home",name:"home",label:"首页",icon:"house",url:"Home"},{path:"/mail",name:"mail",label:"商品管理",icon:"video-play",url:"Mail"},{path:"/user",name:"user",label:"用户管理",icon:"user",url:"User"},{path:"other",label:"其他",icon:"location",children:[{path:"/page1",name:"page1",label:"页面1",icon:"setting",url:"Page1"},{path:"/page2",name:"page2",label:"页面2",icon:"setting",url:"Page2"}]}])const noChildren = computed(()=>list.value.filter(item=>!item.children))const hasChildren = computed(()=>list.value.filter(item=>item.children))
</script>
2.3 导航栏(CommonHeader.vue)
左侧的按钮添加click触发的方法,用于处理菜单折叠事宜。
<template><div class="header"><div class="l-content"><el-button size="small" @click="handleCollapse"><component class="icons" is="menu"></component></el-button><el-breadcrumb separator="/" class="bread"><el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item></el-breadcrumb></div><div class="r-content"><el-dropdown><span class="el-dropdown-link"><img :src="getImageUrl('user')" class="user"/></span><template #dropdown><el-dropdown-menu><el-dropdown-item>个人中心</el-dropdown-item><el-dropdown-item>退出</el-dropdown-item></el-dropdown-menu></template></el-dropdown></div></div>
</template><script setup>import {useAllDataStore} from '@/stores'const store = useAllDataStore()const handleCollapse = () => { store.state.isCollapse = !store.state.isCollapse}const getImageUrl= (user) => { return new URL(`../assets/img/${user}.png`,import.meta.url).href}
</script>
3 状态变更的集中管理
不推荐直接修改 store.state.xxx?
(1)违反了“单一状态更新来源”的原则
Pinia(以及Vuex)鼓励将状态的变更逻辑集中管理,而不是在组件中随意修改。
正确做法:使用actions来定义修改状态的方法。
(2)不利于维护和调试
如果多个组件都直接修改state,你很难追踪是谁改了状态。
没有统一的日志、调试工具支持(比如Vue DevTools中看不到“action被调用”)。
3.1 stores/index.js(pinia)
import { defineStore } from "pinia";
import { ref, computed } from "vue";
function initState(){return {isCollapse:true,}
}
export const useAllDataStore = defineStore("allData", ()=>{// ref() 相当于state属性// computed() 相当于getters// function() 相当于actionsconst state = ref(initState());// 定义 actionfunction toggleCollapse() {state.value.isCollapse = !state.value.isCollapse}return {state,toggleCollapse}
});
3.2 导航栏(CommonHeader.vue)
4 附录
4.1 computed
const isCollapse = computed(() => store.state.isCollapse)
我想要一个值,它始终等于store.state.isCollapse的当前值,一旦它变了,我就跟着变。const isCollapse = store.state.isCollapse
我现在把store.state.isCollapse的值复制一份,以后它变不变都和我没关系了。
4.2 参考附录
参考elementplus官网地址
参考vue3实现通用后台管理(傻瓜式一步一步记录代码实现过程)