在追求速度的今天,我们都是习惯了开箱即用的框架,因为这样,我们只需要在其中添砖加瓦就可以完成一个项目的开发,不用考虑所谓的配置、环境、兼容等等。然而,在使用的时候,我们是否会考虑某些效果的实现原理。比如,接下来要说的中后台管理系统路由的嵌套。
常见的中后台管理系统的布局都是顶部信息栏,左边菜单侧边栏,右边主内容区块,有时候还需要底部页脚区块,通过切换左侧菜单,右侧主内容区变更,而左侧和顶部是不变的,这样的体验也就是常说的SPA应用,当我们登陆超时或者退出登陆时页面跳转到登录页,而此时并没有固定的布局,也就是说跳出了固定的布局渲染区块。
对于上面说到的效果,在react项目里面,可以用到react-router路由嵌套实现,在vue项目里面,可以使用vue-router路由嵌套实现,这里主要阐述vue项目中SPA应用路由嵌套的实现逻辑。
上代码之前先了解一下vue-router嵌套原则,详细配置请移步官方文档。
单层渲染:
当路由匹配到/user/xxx
时,渲染User组件
1 2 3
| <div id="app"> <router-view></router-view> </div>
|
1 2 3 4 5 6 7 8 9 10
| const User = { template: '<div>User {{ $route.params.id }}</div>' } const router = new VueRouter({ routes: [{ path: '/user/:id', component: User }] })
|
嵌套渲染:
当路由匹配到/user/xxx
时,渲染User组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const User = { template: ` <div class="user"> <h2>User {{ $route.params.id }}</h2> <router-view></router-view> </div> ` }
const router = new VueRouter({ routes: [{ path: '/user/:id', component: User, children: [ { path: '', component: UserHome }, { path: 'posts', component: UserPosts }] }] })
|
了解了嵌套路由规则,接下来,初始化vue-cli脚手架项目,安装vue-router,在vue挂载的DOM下添加router-view:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div id="app"> <router-view/> </div> </template> <script> export default { name: 'app', data() { return { } }, components: { }, mounted() { }, methods: { } } </script> <style>
</style>
|
在main.js文件中使用vue-router:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import server from './plugin' import router from './router'
Vue.use(ElementUI); Vue.use(server);
new Vue({ router, render: h => h(App), }).$mount('#app')
|
编写公共组件,包含头部、侧边栏和主内容区块的组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <template> <div> <div class="mdui-appbar mdui-appbar-fixed"> <div class="mdui-toolbar mdui-color-theme"> <a href="javascript:;" class="mdui-btn mdui-btn-icon"><i class="mdui-icon material-icons">menu</i></a> <a href="#/" class="mdui-typo-headline">LOGO</a> <a href="javascript:;" class="mdui-typo-title">标题</a> <div class="mdui-toolbar-spacer"></div> <a href="javascript:;" class="mdui-btn mdui-btn-icon" mdui-tooltip="{content: '搜索'}"> <i class="mdui-icon material-icons">search</i> </a> <a href="javascript:;" class="mdui-btn mdui-btn-icon" mdui-tooltip="{content: '刷新'}"> <i class="mdui-icon material-icons">refresh</i> </a> <a href="javascript:;" mdui-dialog="{target: '#login-modal'}" class="mdui-btn mdui-btn-icon" mdui-tooltip="{content: '登陆或注册'}"> <i class="mdui-icon material-icons">transfer_within_a_station</i> </a> </div> </div> <div class="mdui-drawer" id="drawer"> <ul class="mdui-list" mdui-collapse="{accordion: true}"> <template v-for="(item, index) in sideMenu"> <li class="mdui-collapse-item" :key="index"> <div class="mdui-collapse-item-header mdui-list-item mdui-ripple"> <i class="mdui-list-item-icon mdui-icon material-icons">{{item.icon}}</i> <div class="mdui-list-item-content">{{item.name}}</div> <i class="mdui-collapse-item-arrow mdui-icon material-icons">keyboard_arrow_down</i> </div> <ul class="mdui-collapse-item-body mdui-list mdui-list-dense"> <template v-for="(n, i) in item.children"> <li class="mdui-list-item mdui-ripple" :key="n.name + i"><router-link :to="n.path">{{n.name}}</router-link></li> </template> </ul> </li> </template> </ul> </div> <div class="mdui-container doc-container doc-no-cover"> <router-view/> </div> </div> </template>
<script> import sideMenu from "../../menuConfig"; export default { name: "layouts", data() { return { sideMenu: sideMenu, } }, mounted() {}, methods: {} } </script> <style scoped> </style>
|
这里使用mdui框架,导入menu菜单配置文件,通过vue遍历生成对应的DOM元素,menuConfig可以自行根据需求配置,然后再主内容区块嵌套<router-view/>
。
下面才是重点,不过在重点到来之前我们需要新建router文件夹,并在里面新建index.js配置路由,然后新建page文件用户存放页面模块。
路由配置index.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import Vue from 'vue'; import VueRouter from 'vue-router'; import BookLists from '../pages/bookLists'; import Login from '../pages/login'; import HeaderAside from '../layouts/HeaderAside'; let transformRoutes = [{ path: '/login', component: Login }, { path: '/bookLists', component: HeaderAside, children: [{ path: '', component: BookLists }] }]; Vue.use(VueRouter);
const router = new VueRouter({ routes: transformRoutes });
router.beforeEach((to, from, next) =>{ if (to.path == "/login") { next(); } else { if (localStorage.getItem('token')) { next() } else { next({ path: "/login" }) } } });
export default router;
|
当我们在浏览器输入对应的URL之后,路由拦截器检测本地是否授权存有token,如果存有token即匹配路由,渲染对应的组件,例如,访问/bookLists
,路由配置/bookLists
,对应在根级挂载点#app渲染HeaderAside组件,
然后在HeaderAside组件中router-view渲染children中的BookLists组件,这样达到了嵌套路由的效果;如果不存在token,此时配置/login
,跳转到单层路由渲染,直接在在根级挂载点#app渲染Login组件,因为route不存在children属性,
所以对应的HeaderAside组件中的router-view也不会渲染。这里的路由嵌套transformRoutes
是属于硬编码,我们可以根据需求情况分离出一个模块,通过配置文件自动生成,这样达到可扩展的目的。