前端项目框架搭建随笔---Tab组件的编写

3,258 阅读3分钟

低下头看了看自己的手环。距离自己的flag已经跳票3天了。。。


不为什么,因为我懒#滑稽 #滑稽#滑稽

咳咳,咱们今天进入正题---Tab组件的编写

首先还是先看Tab预览图:

Tab预览图



很简单的功能,很简洁的UI。

首先还是献上Tab html代码和css代码(我css基础不好 各位可以忽略#滑稽)

为了自由度高一点,我们采用 Tab+TabPanel 的方式制作。这种制作方式也是大部分UI框架的制作方式

Tab-HTML代码

<template>
  <div class="tab-wrapper">
    <div class="tab__header">
      <div class="tab__header__item">
        <div style="width: 100%;">
          <div class="tab__item">
            <span>验证码登录</span>
          </div>
          <div class="tab__item">
            <span>密码登录</span>
         </div>
        </div>
      </div>
    </div>
    <div class="tab__content">
      <slot></slot>
    </div>
  </div>
</template>

Tab-CSS代码

<style scoped>
  .iconfont {
    font-size: 1.5rem;
  }

  .tab__header {
    display: flex;
  }

  .tab__header__item {
    margin: 0;
  }

  .tab__header__item > div {
    display: flex;
    flex-direction: row;
    white-space: nowrap;
    transition: transform .3s;
  }

  .tab__item {
    font-size: 1.3rem;
    cursor: pointer;
    margin-right: 1rem;
    flex: 1;
    text-align: center;
  }

  .tab--active > span {
    border-bottom: 2px solid #1890FF;
    color: #1890FF;
    padding-bottom: .5rem;
  }

  .tab__header__btn--left {
    margin-right: .5rem;
    cursor: pointer;
  }

  .tab__header__btn--right {
    margin-left: .5rem;
    cursor: pointer;
  }

  .tab--panel-wrapper {
    display: none;
  }

  .tab--panel-wrapper--active {
    display: block !important;
  }
</style>


TabPanel代码:

<template>
  <div class="tab--panel-wrapper" :name="name">
    <div class="tab--panel-content">
      <slot></slot>
    </div>
  </div>
</template>

<script>
  export default {
    name: "ZbTabPanel",
    props: {
      name: { //Tab模块名
        required: true
      }
    }
  }
</script>

<style scoped>

</style>



先开始制作基础功能:Tab切换

Tab切换功能的制作

首先一上来就会出现理解性的问题:

以往我们写Tab 头部写头部的东西,内容写内容的东西。两者分开来写。便于理解

但如果是这种组件套组件的方式,看起来用法简单了不少。但是增加了编写难度

还有一点,父组件怎么去控制子组件的显示隐藏情况?

那就一步步来呗。

为了方便便于编写起来好理解。我选择内容插入,头部遍历的方式去制作

<template>
  <div class="tab-wrapper">
    <div class="tab__header" ref="tabHeaderItem">
      <div class="tab__header__item">
        <div style="width: 100%;">
          <div class="tab__item"
               @click="tabItemClick(item.name,key)" //tab的点击事件
               @touchstart="tabItemClick(item.name,key)" //tab的移动端点击事件
               v-for="(item,key) in tabList" //循环list
               :class="{'tab--active':activeTab.index===key}"> //如果当前tabtitle的下标 =已激活的tabtitle下标
            <span>{{item.name}}</span> 
          </div>
        </div>
      </div>
    </div>
    <div class="tab__content"
         ref="content">
      <slot></slot>
    </div>
  </div>
</template>


js方面:

data() {
  return {
    tabList: [], //tab标题列表
    activeTab: { //已激活的tab信息
      index: 0, //下标
      name: '' //名称
    }
  }
}


既然是插槽slot,那我们就用点插槽该做的事情 #嘿嘿嘿


于是我在 mounted 函数内,看一下$slot的内容

结果还真的有


不过出现了一个空插槽值。。。但是无关紧要,我们可以加一层空值判断嘛~~

let self = this; //外层新建变量引用this
this.$slots.default.forEach((components) => { //循环default内的内容
  if (components.tag && components.componentOptions) { //如果子元素tag键&&componentOptions有内容。
    self.tabList.push(components.componentOptions.propsData) 
    // 在components.componentOptions这个键内 有propsDate这个属性。我们可以通过这个属性拿到子组件的props值
  }
});
this.$nextTick(() => { //避免data未更新
  this.activeTab = { //给activeTab赋初始值 
    index: 0, //默认选中第一个
    name: this.tabList[0].name //寻找tabList第一个元素 还有他的名字
  };
});

这样切换头部功能实现了。但是底部主体内容无动于衷

所以我们在watch函数内,监听一下activeTab变量的数据变化:

watch: {
  activeTab(newValue, oldValue) {
    this.$refs.content.children[oldValue.index].className = "tab--panel-wrapper";
    this.$refs.content.children[newValue.index].className = "tab--panel-wrapper--active";
  }
}

这样基础内容就大功告成了。


用法参考

<zb-tab>
  <zb-tab-panel name="验证码登录">
    <!--这是验证码登录的内容-->
  </zb-tab-panel>
  <zb-tab-panel name="密码登录">
    <!--这是密码登录的内容-->
  </zb-tab-panel>
</zb-tab>

因为name属性不能少 所以是必填



这样一来遇到个问题,我们的tab是用flex编写的。但是如果tab多了横向滚动怎么办?

这里安利一个滴滴出行@ustbhuangyi 开发的滚动组件: better-scroll

首先我们先安装他:

better-scroll文档

npm install better-scroll --save

接着在vue组件内引入:

import BScroll from 'better-scroll'

这里不做过多介绍。所以我们简单使用。详细使用请看文档

_initScroll: function () {
  new BScroll(this.$refs.tabHeaderItem, {
    scrollX: true, //是否支持X滚动
    bounce: true //是否开启回弹动画
  });
}

挂载函数内

setTimeout(() => {
  this.$nextTick(() => { //避免data未更新
      this._initScroll();
    }
  )
}, 20)

如果看到行内样式有css动画。说明就挂载成功了

这样遇到了一个小bug。挂载成功是可以。但是无法滚动。如图



经查:是因为他挂载的那个元素,我们写个width:100%。导致他丢失了实际长度。无法挂载。

但是去掉width:100%后,元素始终不会平铺开。会左浮动一样的排列



所以我们为了两种都要,加个props外部控制吧~~

props: {
  floatLeft: { //是否开启左浮动模式
    default: false
  }
}

html:

<div class="tab__header__item"
     :style="floatLeft? '': 'width:100%'">


这样一个不算很完美的tab就完成了。希望制作思想可以帮到大家