低下头看了看本身的手环。距离本身的flag已经跳票3天了。。。css
不为何,由于我懒#滑稽 #滑稽#滑稽 html
咳咳,我们今天进入正题---Tab组件的编写vue
首先仍是先看Tab预览图:git
很简单的功能,很简洁的UI。github
首先仍是献上Tab html代码和css代码(我css基础很差 各位能够忽略#滑稽)npm
为了自由度高一点,咱们采用 Tab+TabPanel 的方式制做。这种制做方式也是大部分UI框架的制做方式bash
<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>复制代码
<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>
复制代码
<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切换app
首先一上来就会出现理解性的问题:框架
以往咱们写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多了横向滚动怎么办?
首先咱们先安装他:
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就完成了。但愿制做思想能够帮到你们