# 路由和側邊欄
路由和側邊欄是組織起一個后臺應用的關鍵骨架。
本項目側邊欄和路由是綁定在一起的,所以你只有在`@/router/index.js`下面配置對應的路由,側邊欄就能動態的生成了。大大減輕了手動重復編輯側邊欄的工作量。當然這樣就需要在配置路由的時候遵循一些約定的規則。
## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E9%85%8D%E7%BD%AE%E9%A1%B9)配置項
首先我們了解一些本項目配置路由時提供了哪些配置項。
~~~
//當設置 true 的時候該路由不會再側邊欄出現 如401,login等頁面,或者如一些編輯頁面/edit/1
hidden: true // (默認 false)
//當設置 noRedirect 的時候該路由在面包屑導航中不可被點擊
redirect: 'noRedirect'
//當你一個路由下面的 children 聲明的路由大于1個時,自動會變成嵌套的模式--如組件頁面
//只有一個時,會將那個子路由當做根路由顯示在側邊欄--如引導頁面
//若你想不管路由下面的 children 聲明的個數都顯示你的根路由
//你可以設置 alwaysShow: true,這樣它就會忽略之前定義的規則,一直顯示根路由
alwaysShow: true
name: 'router-name' //設定路由的名字,一定要填寫不然使用<keep-alive>時會出現各種問題
meta: {
roles: ['admin', 'editor'] //設置該路由進入的權限,支持多個權限疊加
title: 'title' //設置該路由在側邊欄和面包屑中展示的名字
icon: 'svg-name' //設置該路由的圖標
noCache: true //如果設置為true,則不會被 <keep-alive> 緩存(默認 false)
breadcrumb: false // 如果設置為false,則不會在breadcrumb面包屑中顯示
}
~~~
**示例:**
~~~
{
path: '/permission',
component: Layout,
redirect: '/permission/index', //重定向地址,在面包屑中點擊會重定向去的地址
hidden: true, // 不在側邊欄線上
alwaysShow: true, //一直顯示根路由
meta: { roles: ['admin','editor'] }, //你可以在根路由設置權限,這樣它下面所以的子路由都繼承了這個權限
children: [{
path: 'index',
component: ()=>import('permission/index'),
name: 'permission',
meta: {
title: 'permission',
icon: 'lock', //圖標
role: ['admin','editor'], //或者你可以給每一個子路由設置自己的權限
noCache: true // 不會被 <keep-alive> 緩存
}
}]
}
~~~
## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E8%B7%AF%E7%94%B1)路由
這里的路由分為兩種,`constantRoutes`和`asyncRoutes`。
**constantRoutes:**代表那些不需要動態判斷權限的路由,如登錄頁、404、等通用頁面。
**asyncRoutes:**代表那些需求動態判斷權限并通過`addRoutes`動態添加的頁面。
具體的會在[權限驗證](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/permission.html)頁面介紹。
> ## TIP
>
> 這里所有的路由頁面都使用`路由懶加載`了 ,具體介紹見[文檔](https://panjiachen.github.io/vue-element-admin-site/zh/guide/advanced/lazy-loading.html)
>
> 如果你想了解更多關于 browserHistory 和 hashHistory,請參看[構建和發布](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/deploy.html)。
>
> 其它的配置和[vue-router](https://router.vuejs.org/zh-cn/)官方并沒有區別,自行查看文檔。
>
> ## 注意事項
>
> 如果這里有一個需要非常注意的地方就是`404`頁面一定要最后加載,如果放在 constantRoutes 一同聲明了`404`,后面的所以頁面都會被攔截到`404`,詳細的問題見[addRoutes when you've got a wildcard route for 404s does not work](https://github.com/vuejs/vue-router/issues/1176)
## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E4%BE%A7%E8%BE%B9%E6%A0%8F)側邊欄
本項目側邊欄主要基于`element-ui`的`el-menu`改造。
前面也介紹了,側邊欄是通過讀取路由并結合權限判斷而動態生成的,而且還需要支持路由無限嵌套,所以這里還使用到了遞歸組件。
代碼地址
[@/views/layout/components/Sidebar](https://github.com/PanJiaChen/vue-element-admin/tree/master/src/layout/components/Sidebar)
這里同時也改造了`element-ui`默認側邊欄不少的樣式,所有的 css 都可以在[@/styles/sidebar.scss](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/styles/sidebar.scss)中找到,你也可以根據自己的需求進行修改。
**這里需要注意一下**,一般側邊欄有兩種形式即:`submenu`和 直接`el-menu-item`。 一個是嵌套子菜單,另一個則是直接一個鏈接。如下圖:

在`Sidebar`中已經幫你做了判斷,當你一個路由下面的`children`聲明的路由大于>1 個時,自動會變成嵌套的模式。如果子路由正好等于一個就會默認將子路由作為根路由顯示在側邊欄中,若不想這樣,可以通過設置在根路由中設置`alwaysShow: true`來取消這一特性。如:
~~~
// No submenu, because children.length===1
{
path: '/icon',
component: Layout,
children: [{
path: 'index',
component: ()=>import('svg-icons/index'),
name: 'icons',
meta: { title: 'icons', icon: 'icon' }
}]
},
// Has submenu, because children.length>=1
{
path: '/components',
component: Layout,
name: 'component-demo',
meta: {
title: 'components',
icon: 'component'
},
children: [
{ path: 'tinymce', component: ()=>import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }},
{ path: 'markdown', component: ()=>import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }},
]
}
~~~
unique-opened
你可以在[Sidebar/index.vue](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/layout/components/Sidebar/index.vue)中設置`unique-opened`來控制側邊欄,是否只保持一個子菜單的展開。
## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E5%A4%9A%E7%BA%A7%E7%9B%AE%E5%BD%95-%E5%B5%8C%E5%A5%97%E8%B7%AF%E7%94%B1)多級目錄(嵌套路由)
如果你的路由是多級目錄,如本項目[@/views/nested](https://github.com/PanJiaChen/vue-element-admin/tree/master/src/views/nested)那樣, 有三級路由嵌套的情況下,不要忘記還要手動在二級目錄的根文件下添加一個`<router-view>`。
如:[@/views/nested/menu1/index.vue](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/nested/menu1/index.vue),原則上有多少級路由嵌套就需要多少個`<router-view>`。

## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E7%82%B9%E5%87%BB%E4%BE%A7%E8%BE%B9%E6%A0%8F-%E5%88%B7%E6%96%B0%E5%BD%93%E5%89%8D%E8%B7%AF%E7%94%B1)點擊側邊欄 刷新當前路由
在用 spa(單頁面應用) 這種開發模式的之前,用戶每次點擊側邊欄都會重新請求這個頁面,用戶漸漸養成了點擊側邊欄當前路由來刷新 view 的習慣。但現在 spa 就不一樣了,用戶點擊當前高亮的路由并不會刷新 view,因為 vue-router 會攔截你的路由,它判斷你的 url 并沒有任何變化,所以它不會觸發任何鉤子或者是 view 的變化。[issue](https://github.com/vuejs/vue-router/issues/296)地址,社區也對該問題展開了激烈討論。

尤大本來也說要增加一個方法來強刷 view,但后來他又改變了心意/(ㄒ o ㄒ)/~~。但需求就擺在這里,我們該怎么辦呢?他說了不改變 current URL 就不會觸發任何東西,那我可不可以強行觸發你的 hook 呢?上有政策, 下有對策我們變著花來 hack。方法也很簡單,通過不斷改變 url 的 query 來觸發 view 的變化。我們監聽側邊欄每個 link 的 click 事件,每次點擊都給 router push 一個不一樣的 query 來確保會重新刷新 view。
~~~
clickLink(path) {
this.$router.push({
path,
query: {
t: +new Date() //保證每次點擊路由的query項都是不一樣的,確保會重新刷新view
}
})
}
~~~
ps:不要忘了在`router-view`加上一個特定唯一的`key`,如`<router-view :key="$route.path"></router-view>`, 但這也有一個弊端就是 url 后面有一個很難看的`query`后綴如`xxx.com/article/list?t=1496832345025`
你可以從前面的 issue 中知道還有很多其它方案。我本人在公司項目中,現在采取的方案是判斷當前點擊的菜單路由和當前的路由是否一致,但一致的時候,會先跳轉到一個專門 Redirect 的頁面,它會將路由重定向到我想去的頁面,這樣就起到了刷新的效果了。
**相關例子**

點擊圖片所示的全局 size 大小切換按鈕,你會發現 頁面`app-main`區域進行了刷新。它就是運用了重定向到`Redirect`頁面之后再重定向回原始頁面的方法。
點擊的時候重定向頁面至`/redirect`
~~~
const { fullPath } = this.$route
this.$router.replace({
path: '/redirect' + fullPath
})
~~~
`redirect`頁面在重定向回原始頁面
~~~
// redirect.vue
// https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/redirect/index.vue
export default {
beforeCreate() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
~~~
## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E9%9D%A2%E5%8C%85%E5%B1%91)面包屑
本項目中也封裝了一個面包屑導航,它也是通過`watch $route`變化動態生成的。它和 menu 也一樣,也可以通過之前那些配置項控制一些路由在面包屑中的展現。大家也可以結合自己的業務需求增改這些自定義屬性。比如可以在路由中聲明`breadcrumb:false`,讓其不在 breadcrumb 面包屑顯示。

代碼地址
[@/components/Breadcrumb](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/Breadcrumb/index.vue)
## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E4%BE%A7%E8%BE%B9%E6%A0%8F%E6%BB%9A%E5%8A%A8%E9%97%AE%E9%A2%98)側邊欄滾動問題
之前版本的滾動都是用 css 來做處理的
~~~
overflow-y: scroll;
::-webkit-scrollbar {
display: none;
}
~~~
首先這樣寫會有兼容性問題,在火狐或者其它低版本瀏覽器中都會比較不美觀。其次在側邊欄收起的情況下,受限于`element-ui`的`menu`組件的實現方式,不能使用該方式來處理。
所以現版本中使用了`el-scrollbar`來處理側邊欄滾動問題。
代碼地址
[@/components/Sidebar](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/layout/components/Sidebar/index.vue)
## [#](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#%E4%BE%A7%E8%BE%B9%E6%A0%8F-%E5%A4%96%E9%93%BE)側邊欄 外鏈v3.8.2+
你也可以在側邊欄中配置一個外鏈,只要你在`path`中填寫了合法的 url 路徑,當你點擊側邊欄的時候就會幫你新開這個頁面。
例如:
~~~
{
"path": "external-link",
"component": Layout,
"children": [
{
"path": "https://github.com/PanJiaChen/vue-element-admin",
"meta": { "title": "externalLink", "icon": "link" }
}
]
}
~~~