api server已经有一个最基本的框架了,我们回到前端,开始制作一个用户管理界面吧。
路由管理与Layout
从main.js可以看到渲染的入口是App.vue
import App from './App'
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})
App.vue里的模板很简单,就是一个router-view
<template>
<div id="app">
<router-view />
</div>
</template>
结合之前分析过的permission.js,中间有个白名单:
如果是未登录用户(判断依据就是store中没有token),访问的url又不在这个白名单中,就强行跳转到login页面
对于已经登录过,但store中还没有用户角色信息的情况,先调用info接口获取角色信息,根据添加动态路由配置,最后在将页面路由到指定的url
const { roles } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
现在我们知道前端的路由配置都是在store/permission.js中的generateRoutes中实现的,查看相关代码,generateRoutes会根据用户角色生成对应的动态路由规则,最后将这些规则与constantRoutes中定义的静态规则合并到一起。
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
constantRoutes和asyncRoutes都是在router/index.js中定义的,router组件也是在这里初始化的,可以看出,初始化时只配置了静态规则:
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({
y: 0
}),
routes: constantRoutes
})
const router = createRouter()
constantRoutes静态规则是一个大的数组,包含了url和对应view组建的映射关系
export const constantRoutes = [{
path: '/redirect',
component: Layout,
hidden: true,
children: [{
path: '/redirect/:path*',
component: () => import('@/views/redirect/index')
}]
},
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
asyncRoutes也是类似的数组,只不过额外增加了一些访问控制信息
export const asyncRoutes = [
path: '/permission',
component: Layout,
redirect: '/permission/page',
alwaysShow: true, // will always show the root menu
name: 'Permission',
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [{
path: 'page',
大概了解一下前端路由生成机制,我们发现几乎所有路由都指向了Layout组件,继续查看Layout源码layout/index.vue,这个组件是整个页面布局的入口,左边的siderbar,顶部的navbar,内容区的app-main,以及右边的浮动设置按钮right-panel。
<template>
<div :class="classObj" class="app-wrapper">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<sidebar class="sidebar-container" />
<div :class="{hasTagsView:needTagsView}" class="main-container">
<div :class="{'fixed-header':fixedHeader}">
<navbar />
<tags-view v-if="needTagsView" />
</div>
<app-main />
<right-panel v-if="showSettings">
<settings />
</right-panel>
</div>
</div>
</template>
跟进看一下layout/components/AppMain.vue的实现,发现他的html模板里又包含了一个router-view,这就能让我更好地理解嵌套路由的配置了。
<template>
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews">
<router-view :key="key" />
</keep-alive>
</transition>
</section>

了解Layout的结构,就可以知道,layout/components/siderbar/会根据路由规则实现siderbar中的菜单项,layout/components/Navbar实现了navbar,至于app-main,根据各项路由的component参数可知,一般都放在views/目录下