1、定义组件:
在html中定义
t-nav组件定义为最外层导航栏包裹器,nav-item组件包裹每一项导航名字,并且每个nav-item组件都声明一个name,用以标识每一项导航。sub-nav组件包裹含有下拉列表的导航项。
首页
关于
企业文化
开发团队
联系电话
联系方式
移动
联通
电信
招聘
2、基础功能逻辑 :
t-nav组件定义slot插槽,主要负责nav-item和sub-nav组件的传递。vertical代表上面html中:selected的初始选中值。
nav-item组件定义导航每一项,可点击,点击后选中值为当前项,并展示相关样式。
sub-item组件定义一组下拉导航,可点击,根据横向导航或是竖向导航展示不同下拉状态。
3、支持横向导航竖向导航
我们在t-nav组件标签上定义 vertical,有vertical则表示竖向导航,无则表示横向导航。然后将vertical传给nav组件和sub-nav组件,通过判断更改组件内部flex排列样式即可,这里css反而比较重点也比较难写。
t-nav组件:
nav组件:
vertical是true则CSS展示flex-direction: column。
sub-nav组件:
通过vertical判断展示哪段html,即横向或是竖向,并展示相应CSS。
4、支持导航click展开下拉列表
通过sub-nav下拉导航组件来展示,v-show="open" 是否显示下拉列表。为什么用v-show,而不是v-if。因为v-if从false到true的状态会触发生命周期钩子:created、updated。v-if并不是能一开始跟随父组件一起经历所有的钩子事件,而是单独从false状态到true状态时触发的。因此在数据传递的过程中并不是同步的,会出现BUG。
5、支持下拉列表click展开二级下拉列表,可任意层级
原理是通过sub-nav组件与nav-item组件嵌套即可实现,但sub-nav下面的nav-item与t-nav下面的sub-nav不属于同级,层级比较深,相当于sub-nav下面的nav-item可能是层级很深的子孙组件,存在跨组件通讯的问题。
怎么解决这个问题?使用依赖注入provide/inject,将父级的数据注入到其底下所有的子子孙孙当中,这样无论子孙组件层级多深,我们都可以调用到父级的数据并操作。
联系方式
移动
联通
电信
联系方式
移动
联通
电信
父组件t-nav:
export default {
name: 't-nav',
provide() { //依赖
return {
root: this,
vertical: this.vertical
}
},
}
子孙组件nav-item:
export default {
name: 't-nav-item',
inject: ['root'], //注入
mounted() {
this.root.addItem(this)
},
}
子孙组件sub-nav:
export default {
inject: ['root', 'vertical'], //注入
computed: {
active() {
return this.root.namePath.indexOf(this.name) > -1
},
},
}
6、配有展开动画
下拉的展开动画,通过vue中的transiton动画组件即可完成。但这里我们特意用了transition的动画钩子实现的,就是定义动画开始、动画结束等等相关时调用钩子来做一些操作,这在写复杂的动画样式时非常有用。
methods: {
enter(el, done) {
let {height} = el.getBoundingClientRect()
el.style.height = 0
el.getBoundingClientRect()
el.style.height = `${height}px`
el.addEventListener('transitionend', ()=>{
done()
})
},
afterEnter(el) {
el.style.height = 'auto'
},
leave(el, done) {
let {height} = el.getBoundingClientRect()
el.style.height = `${height}px`
el.getBoundingClientRect()
el.style.height = 0
el.addEventListener('transitionend', ()=>{
done()
})
},
afterLeave(el) {
el.style.height = 'auto'
},
}
&-popover {
position: absolute;
top: 100%;
left: 0;
border: 1px solid black;
white-space: nowrap;
background: white;
margin-top: 4px;
box-shadow: 0 0 3px fade_out(black, 0.8);
border-radius: $border-radius;
font-size: $font-size;
color: $light-color;
min-width: 8em;
&.vertical {
position: static;
border-radius: 0;
border: none;
box-shadow: none;
transition: height .3s;
overflow: hidden;
}
}
这里钩子的使用时,要注意浏览器的对于样式的多次修改会执行合并操作,浏览器只会执行最后一个样式。所以当我们在短时间内要执行多个样式的时候,需要在样式中间做点动作,让浏览器无法合并样式操作。
比如:
el.style.height = `${height}px`
el.getBoundingClientRect()
el.style.height = 0