菜單即路由,共享配置html
views
目錄中存放業務組件;業務模塊分文件夾存放,路由主組件使用index
命名存放在以路由名的目錄下。vue
views **業務組件 └── base 業務分組目錄,對應router目錄中的菜單組 ├── about 業務功能 │ ├── index.vue 業務路由頁面主組件 │ └── ...others.vue 相關的分塊組件 ├── welcome │ └── index.vue └── index.vue 子路由組件,在菜單開啓子路由時可用
每個配置文件對應一組菜單,文件名對應views下面的子目錄,配置結構以下:webpack
// base.ts const menu: IMenu = { name: 'base', // 菜單名稱,對應views目錄下的子目錄 title: '基本信息', // 菜單標題 isPath: true, // 是否爲訪問父路徑,默認狀況下菜單下的目錄爲一級路由,開啓此項將讀取目錄下index.vue做爲子路由組件,文件不存在時自動生成組件。 routes: [ // 菜單目錄 { name: 'about', // 菜單名稱,對應路由名稱 title: '關於', // 菜單標題,對應路由meta信息 filename: 'about', // 組件文件名,默認爲name path: 'about', // 訪問路徑,配置方式同vue-router,默認爲name ...RouterConfig // 其它vue-router的路由配置 }, { name: 'welcome', title: '歡迎' } ] } export default menu
menuToRoute
方法將菜單配置轉換爲路由配置 web
參數vue-router
menu
:單個菜單配置對象或多個配置對象數組isRoot
:是否爲根路由;路由配置中根路由path
須要以/
開頭// menuToRoute.ts export default function formatRoute(menu: IMenu | IMenu[], isRoot?: boolean) { let _routes: RouteConfig[] = [] if (Array.isArray(menu)) { for (const item of menu) { _routes = _routes.concat(formatRoute(item, isRoot)) } return _routes } const { name: menuName, isPath, routes } = menu const children = routes.map(route => { const { name, filename = name, path, title, meta, ...config } = route return { name, path: (!isPath && isRoot ? '/' : '') + (path || name), component: () => // 使用chunks方法對代碼按照菜單分塊打包,詳見後面說明 chunks(menuName, filename).then(e => { // 在使用keep-alive動態緩存include屬性時須要組件name // 加上'v-'前綴避免「Do not use built-in or reserved HTML elements as component id」 ;(e.default.options || e.default).name = 'v-' + name return e }), meta: title ? { title, ...meta } : meta ...config, } }) _routes = isPath ? [ { path: (isRoot ? '/' : '') + menuName, // 若是使用二級路由,讀取菜單目錄下的index爲子路由組件,沒有找到使用默認組件 component: () => chunks(menuName, 'index').catch(() => subView), children } ] : children return _routes }
與其它路由配置結合使用:typescript
// @/router/routes.ts import Home from '@/views/Home.vue' /* 導入全部菜單配置數組 */ import menu from './menu/' import menuToRoute from './menuToRoute' const ISROOT = true const routes = [ { path: '/', name: 'home', component: Home }, ...menuToRoute(menu, ISROOT) ] export default routes
路由對應的組件使用 webpack的import()
方法(查看官方文檔)懶加載,可根據需求修改menuToRoute.ts
文件中的chunk
方法進行代碼切割打包。api
通常小的項目中,能夠直接使用
import( `@/views/${name}/index` )
就能夠了,此方法將每一個目錄下的index默認導出類型文件爲入口將相關引用的文件進行打包;結合前面約定路由主組件以index命名的規範,能夠避免將其它沒有引用到的文件打包。
/** * menuToRoute.ts * 代碼切割打包 * @param type 菜單組(文件夾)名稱 * @param name 路由名稱或路由對應文件名 */ function chunks(type: string, name: string) { switch (type) { case 'guide': // guide菜單下的路由若是沒有指定filename,將默認導入md文件,因爲md非默認導入類型,因此須要加上擴展名 if (!/(^index)|(\.\w+$)/.test(name)) { name += '.md' } return import(/* webpackChunkName: 'guide' */ `@/views/guide/${name}`) case 'example': // 將views/example/下的全部vue和tsx文件打成一個包 return import( /* webpackChunkName: 'example',webpackMode: "lazy-once",webpackInclude: /\.(vue|tsx)$/ */ `@/views/example/${name}` ) default: // 將views目錄下(包括子目錄)全部index文件的默認導入類型分塊打包 return import( `@/views/${name}/index` ) } }
應用到elment-ui的NavMenu菜單導航組件示例:數組
<el-menu :default-active="$route.name" @select="$router.push({ name:$event })"> <el-menu-item index="home"> <template slot="title">首頁</template> </el-menu-item> <el-submenu :index="menu.name" v-for="menu of menuList" :key="menu.name"> <template slot="title">{{ menu.title }}</template> <el-menu-item :index="item.name" v-for="item of menu.routes" :key="item.name"> {{ item.title }} </el-menu-item> </el-submenu> </el-menu>
權限過濾緩存
/** * @param menuList 後臺配置的全部權限信息列表 * @param roleList 用戶多個角色信息 **/ function buildUserRule (menuList, roleList) { // 合併角色權限列表 const menuIdList = [].concat(roleList.map(role=>role.menuIdList)) // 過濾生成用戶權限 const rules = menuList.filter(item => menuIdList.includes(item.id)) // 經過權限信息中的id和parentId對應關係生成目錄樹結構 return formatTree(rules) }
將生成的權限樹結構存放在store中,推薦格式以下:bash
{ 菜單名:{ // 對應菜單組 目錄名:{ // 對應路由 tab名:{ // 路由頁面中多個頁籤 edit: true // 操做按鈕 delete: true } } } }
經過這種格式在菜單顯示的時候方便進行權限過濾,進入到某個路由時也方便取到當前路由下的權限進行權限適配
原創文章,轉載請聲明,歡迎一塊兒討論! @nicefan.cn