vue切换菜单时不需要页面刷新_Vue.js - 动态加载用户菜单样例(解决F5刷新后Store数据丢失问题)...

通常来说,用户在登录成功后会在首页左侧或者上方显示一个用户菜单。而这个菜单数据是根据用户的角色动态加载的,即不同身份的用户登录成功后看到的菜单是不一样的。下面通过样例演示如何实现菜单的加载与展示。

一、后端部分

(1)后端接口实现比较容易,先根据登录用户的 id 查询该用户具有的角色,在根据角色信息查看对应的 Menu,最后将 Menu 返回到前端。

(2)这里假设后端接口为 /sysmenu,返回的数据格式如下:

(1)这里菜单一共有两级,结构上采用嵌套的形式,关于如何查询树形结构的数据,可以参考我之前写的文章:

name:菜单名称

component:菜单对应的 vue 模块名(客户端会根据这个名字记载实际的 component 组件)

path:菜单对应的 vue 模块路径

[

{

"id":2,

"path":"/home",

"component":"Home",

"name":"人员管理",

"iconCls":"fa fa-user-circle-o",

"children":[

{

"id":null,

"path":"/emp/basic",

"component":"EmpBasic",

"name":"基本资料",

"iconCls":null,

"children":[

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

}

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

},

{

"id":5,

"path":"/home",

"component":"Home",

"name":"统计管理",

"iconCls":"fa fa-bar-chart",

"children":[

{

"id":null,

"path":"/sta/all",

"component":"StaAll",

"name":"综合信息统计",

"iconCls":null,

"children":[

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

},

{

"id":null,

"path":"/sta/pers",

"component":"StaPers",

"name":"人事信息统计",

"iconCls":null,

"children":[

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

}

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

},

{

"id":6,

"path":"/home",

"component":"Home",

"name":"系统管理",

"iconCls":"fa fa-windows",

"children":[

{

"id":null,

"path":"/sys/basic",

"component":"SysBasic",

"name":"基础设置",

"iconCls":null,

"children":[

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

},

{

"id":null,

"path":"/sys/log",

"component":"SysLog",

"name":"日志管理",

"iconCls":null,

"children":[

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

}

],

"meta":{

"keepAlive":false,

"requireAuth":true

}

}

]

二、前端部分

1,初始路由设置(router/index.js)

系统初始路由只有一个 /home (即进入首页面)。等后面菜单完毕后,会根据菜单项自动添加对应的路由以及模块。

import Vue from 'vue'

import Router from 'vue-router'

import Home from '@/components/Home'

Vue.use(Router)

export default new Router({

routes: [

/* {

path: '/',

name: 'Login',

component: Login,

hidden: true

},*/

{

path: '/home',

name: 'Home',

component: Home

}

]

})

2,创建 store 用来保存菜单数据(store/index.js)

首先在 store 中创建一个 routes 数组,这个是一个空数组,后面我们将会把服务端返回的 JSON 格式的菜单数据保存在 store 中,然后各个 Vue 页面根据 store 中的数据来渲染菜单。

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({

state: {

routes: []

},

mutations: {

initMenu(state, menus){

state.routes = menus;

}

}

});

3,菜单初始化工具类(utils/menuUtils.js)

(1)该工具类主要用于初始化菜单,其中一个重要的工作是将服务器返回的 JSON 格式的数据转成 router 需要的格式。因为服务端返回的 component 是一个字符串,而 router 中需要的却是一个组件,我们要在这里动态加载相应的组件:

import axios from 'axios';

// 请求菜单数据并初始化

export const initMenu = (router, store)=> {

// 首先判断 store 中数据是否存在,如果存在,则说明这次跳转是正常的跳转

// 而不是用户按F5键或者直接在地址栏输入某个地址进入的,这时直接返回,不必执行菜单初始化

if (store.state.routes.length > 0) {

return;

}

// 若 store 中不存在菜单数据,则需要初始化数据

axios.get("/sysmenu").then(resp=> {

if (resp && resp.status == 200) {

// 将服务器返回的 JSON 格式的数据转成 router 需要的格式

var fmtRoutes = formatRoutes(resp.data);

// 将准备好的数据动态添加到路由中

router.addRoutes(fmtRoutes);

// 同时也将数据存到 store 中

store.commit('initMenu', fmtRoutes);

}

})

}

// 将服务器返回的 JSON 转为 router 需要的格式

export const formatRoutes = (routes)=> {

let fmRoutes = [];

routes.forEach(router=> {

let {

path,

component,

name,

meta,

iconCls,

children

} = router;

if (children && children instanceof Array) {

// 如果有子节点则递归转换

children = formatRoutes(children);

}

let fmRouter = {

path: path,

// 根据服务器返回的 component 动态加载需要的组件

component(resolve){

if (component.startsWith("Home")) {

require(['../components/' + component + '.vue'], resolve)

} else if (component.startsWith("Emp")) {

require(['../components/emp/' + component + '.vue'], resolve)

} else if (component.startsWith("Sta")) {

require(['../components/statistics/' + component + '.vue'], resolve)

} else if (component.startsWith("Sys")) {

require(['../components/system/' + component + '.vue'], resolve)

}

},

name: name,

iconCls: iconCls,

meta: meta,

children: children

};

fmRoutes.push(fmRouter);

})

return fmRoutes;

}

(2)红框部分为项目里菜单对应的各个组件位置:

4,项目主入口代码(main.js)main.js中除了将前面定义的 route、store 引入外,还需要开启一个路由全局守卫,在每次访问某个页面前都去加载一次菜单数据:

1,为什么每次访问页面前都需要加载一次菜单数据?

通常情况下,我们只在登录成功之后请求一次菜单资源,然后将 JSON 数据保存在 store 中,以便下一次使用。但是这样会有一个问题:

假如用户登录成功之后,单击 Home 页的某一个按钮,进入某一个子页面中,然后按一下 F5键进行刷新,这个时候就会出现空白页面,因为按 F5 键刷新之后 store 中的数据就没了。

2,解决F5刷新后Store数据丢失问题?

要解决这个问题有如下两种方案:

方案一,不要将菜单资源保存到 store 中,而是保存到 localStorage 中,这样即使按 F5 键刷新之后数据还在。由于菜单资源是非常敏感的,因此不建议将其保存到本地,故舍弃。

方案二,直接在每一个页面的 mounted 方法中都加载一次菜单资源。但这种做法工作量有点大,而且也不易维护,这里可以使用路由中的导航守卫来简化这个方案的工作量。

import Vue from 'vue';

import App from './App';

import router from './router';

import store from './store';

import {initMenu} from './utils/menuUtils'

Vue.config.productionTip = false;

import ElementUI from 'element-ui';

import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

// 配置一个全局前置守卫

router.beforeEach((to, from, next)=> {

// 首先判断目标页面是不是Login,若是Login页面,则直接通过,因为登录页不需要菜单数据

if (to.name == 'Login') {

next();

return;

}

// 判断当前用户是否已经登录,否则跳回登录页

// ........

// 先初始化菜单数据

initMenu(router, store);

// 再进入下一个页面

next();

}

)

new Vue({

el: '#app',

router,

store,

components: { App },

template: ''

})

5,主视图代码(Home.vue)

菜单渲染操作在 Home.vue组件中完成,并且菜单点击后里面的子路由视图会进行切换:

动态菜单 DEMO

{{item.name}}

style="padding-left: 30px;width: 170px;text-align: left"

v-for="child in item.children"

:index="child.path"

:key="child.path">{{child.name}}

首页

export default{

methods: {

},

data(){

return {

}

},

computed: {

// 在计算属性中返回 routes 数据

routes(){

return this.$store.state.routes

}

}

}

.home-container {

height: 100%;

position: absolute;

top: 0px;

left: 0px;

width: 100%;

}

.home-header {

background-color: #20a0ff;

color: #333;

text-align: center;

display: flex;

align-items: center;

justify-content: space-between;

box-sizing: content-box;

padding: 0px;

}

.home-aside {

background-color: #ECECEC;

}

.home_title {

color: #fff;

font-size: 22px;

display: inline;

margin-left: 8px;

}

.el-submenu .el-menu-item {

width: 180px;

min-width: 175px;

}

6,运行效果

(1)默认访问 /home首页显示效果如下:

(2)点击左侧菜单栏自动切换右侧组件,并且无论当前在那个页面,按下 F5 刷新后菜单都不会丢失。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值