用vue写轮子的一些心得(六)——nav导航栏组件

用vue写轮子的一些心得(六)——nav导航栏组件_第1张图片

需求分析

  • 支持横向导航竖向导航;
  • 支持导航click展开下拉列表;
  • 支持下拉列表click展开二级下拉列表,可任意层级;
  • 配有展开动画;

 

方法实现

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

 

你可能感兴趣的:(VUE)