需求
- 根据router表生成导航内容,实现相关效果,包括:
- 主目录点击背景变色
- 子目录点击变颜色
- url输入相关路由,导航也实现1中效果;
技术栈
- vue
- element
- vuex
- node (path模块)
- router全局导航守卫
- 项目架构:vue-element-template
实现
根据router生成menu
routerMap结构为简单的二维数组,根据需求,最多二维,不考虑更深的嵌套及外链情况;
路由详情请参考路由配置项及示例。
注册组件Navbar
这就不说了。。
应用router数据生成el-tabs
获取router数据
computed: {
routes() {
return this.$router.options.routes
}
}
复制代码
渲染el-tabs,将非隐藏的router展示出来
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane
v-for="route in routes"
class="slideInUp"
v-if="!route.hidden&&route.children"
:name="route.path"
:label= "route.name"
>
</el-tab-pane>
</el-tabs>
复制代码
说到这还没什么难得,接下来问题来了。
问题一:动态渲染label
为icon+text
label
作为el-tab-pane
的属性接收的是字符串,但是我要的label,不仅要文字,还要在上面加上我的icon
. 折腾许久无果后无奈只好给element报了个feature。终于在此找到了思路: label slot
.
<div slot="label">
<svg-icon class="icons" :icon-class="iconClassName(route)" />
<span class="nav-title">{{route.name}}</span>
</div>
复制代码
在el-tab-pane
中间填上slot
,我的icon们果然乖乖出现了! 开森!
sub-menu生成
因为用的el-tab
做navbar
,submenu生成将router.children
放到el-tab-pane之间即可
问题二:动态渲染active class
根据需求,点击submenu后颜色就要发生变化,正好应用一下动态绑定class:
<li
v-for="(item, key, index) in route.children"
:key="item.name"
:id = "index"
style="cursor: pointer;"
:class="{'sub-active':activeIndex == item.path}"
@click="activeIndex = item.path, clickLink(item, $event)">
{{item.name}}
</li>
复制代码
根据path的唯一性,点击时activeIndex
更改为当前的path
,当前submenu
会添加上activeIndex
类.
问题三:点击submenu路由更改
导航的重要作用就是控制路有更改,应用path.resolve
可以生成当前点击的路径。li
中添加的clickLink
方法为:
const path = this.resolvePath(routePath)
this.$router.push(path)
复制代码
resolvePath
为:
resolvePath(routePath) {
return path.resolve(this.basePath, routePath)
}
复制代码
因为this.$router.push
可以实现路由跳转,但是当前的跳转路径只解析出了子目录;
更正方案: 在el-tab-pane
添加:index="resolvePath(route.path)"
,在最初渲染时获取相应的父级path。 之后二级路由就变成了: http://localhost:9528/#/task/index
,嗯,是哀家想要的样子~
基本的功能以为就这样实现了,知道我手动输入了一下路径,,,
根据输入的path更新tab中对应项
active tab
由数据activeName
控制; 子菜单activeIndex
项绑定的是各自的path
.
失败尝试一:
添加watch,监控router变化更改activeName
以及activeIndex
,未果;
失败尝试二:
应用mixins
生成局部钩子函数,覆盖activeName
以及activeIndex
,未果;
失败原因: 应用路由钩子的思路是对的,但必须全局守卫,然后发现路由变化时更新activeName
以及activeIndex
对应的值,这时候再用store
获取新值进行覆盖。
问题四:手动输入的路径渲染menu
- store中添加menu
const menu = {
state: {
menumain: '/',
menusub:'overview'
},
mutations: {
updatemain (state,n) {
state.menumain = n
},
updatesub (state,n) {
state.menusub = n
}
}
}
export default menu
复制代码
添加守卫函数:
router.beforeEach((to, from, next) => {
// add menu change data
let paths = to.path.trim().split('/')
let activeName =''
let activeIndex =''
activeName = !!paths[2]==true ? '/'+ paths[1] : '/'
store.commit('updatemain', activeName)
activeIndex = !!paths[2]==true ? paths[2] : paths[1]
store.commit('updatesub', activeIndex)
//end menu
})
复制代码
navbar中更新数据:
computed: {
newActiveName() {
return this.$store.getters.menumain;
},
newActiveSubMenu() {
return this.$store.getters.menusub;
}
},
watch: {
newActiveName(val){
this.activeName = val
},
newActiveSubMenu(val) {
this.activeIndex = val
}
}
复制代码
通过计算属性发现path更改并获取新值,然后watch监测到数据变化将新值赋给activeName
以及activeIndex
。
看下结果吧!
多说一句:burp是一款非常牛逼的安全产品,有需要的公司可以联系我洽谈试用。 目前,安全元素正在 招聘前端工程师,有意向的也可直接加我微信咨询。忙到半夜总算实现了,开始以为很难,做的很慢,每次搞定一个小点又觉得很容易。还是“难者不会,会者不难”吧。
另:vue中很多设计都会有意想不到的用处,比如这次用到的插槽,动态绑定class;回想当年勇jq遍历li删掉active再给某一项添加active class的日子还历历在目。数据驱动解放dom操作,yeah~
Reference:
本文权当组件书写笔记,多谢各位指教~
未解决的问题
- nested路由情况:可以在路由钩子函数里做递归处理
- 设备检查及手机端适配
- vue相关源码深入
bug修复
-
basepath在点击tab时生成,导致如果动态跳转到该path下, basepath为空,再点击tab下子目录只有子路由,会跳转到404;
fix: 将basepath生成在click item时同步生成
clickLink(item, e, route) { let childPath = item.path if (!this.isExternalLink(childPath)) { e.preventDefault() let parentPath = this.resolvePath(route.path) const finalPath = path.resolve(parentPath, childPath) this.$router.push(finalPath) } } 复制代码
Aboutme
Author: Yanni Jia
Nickname: 非常兔
Wechat: yanni_it
Email: 385067638@qq.com