组件说明:
实现 tab 切换。
效果展示:
实现 tab 切换,改变激活样式,切换到对应的页面
以上 tab 切换功能在前端开发中司空见惯。各种现存的组件也随手拈来。在 Vue 中,有配套的 element-ui 组件,也有 vue-ant-design。
element-ui 中 el-tabs 效果如下:
vue-ant-design 中 a-tabs 效果如下:
但是使用现存组件面临的问题如下
该组件有两种使用方式。
<template>
<el-tab defaultKey="1" @on-click="changeTab">
<el-tab-panes actKey="1" label="全部"></el-tab-panes>
<el-tab-panes actKey="2" label="推荐"></el-tab-panes>
<el-tab-panes actKey="3" label="最新"></el-tab-panes>
</el-tab>
<div>只有我一个页面,更新数据源即可</div>
</template>
<script>
export default{
data(){
return{
}
},
methods:{
changeTab(item,index){
//调数据
}
}
}
</script>
<template>
<el-tab defaultKey="1" @on-click="changeTab">
<el-tab-panes actKey="1" label="全部">
<div>页面1</div>
</el-tab-panes>
<el-tab-panes actKey="2" label="推荐">
<div>页面2</div>
</el-tab-panes>
<el-tab-panes actKey="3" label="最新">
<div>页面3</div>
</el-tab-panes>
</el-tab>
</template>
<script>
export default{
data(){
return{
}
},
methods:{
changeTab(item,index){
//调数据
}
}
}
</script>
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
defaultKey | 默认选中的tab | String | 1 |
actKey | 每个tab的唯一key | String | 无 |
label | 每个tab的标题 | String | 无 |
on-click | tab 被选中时触发 | 点击按钮的回调函数 | (e: Object): void |
实现思路
(1) tab 标签文本样式高亮;
(2) tab 下面的下划线调整到相应位置;
(3) tab 对应的内容切换。
<template>
<div>
<div class="tabs">
<div ref="line" class="tab-line"></div>
<div :class="[activeKey == item.actKey? 'active-tab' : 'tab']" @click="changeTab($event,item,index)" v-for="(item,index) in childList">{
{
item.label}}</div>
</div>
<slot></slot>
</div>
</template>
<script>
let self;
export default {
name: "ElTab",
data(){
return {
childList:[],
activeKey:this.defaultKey,//将初始化tab赋值给activeKey
slideWidth:0
}
},
//获取子组件传过来的激活tab
props:{
defaultKey:{
type: String,
default: "1"
}
},
created(){
self = this;
},
mounted(){
//循环tab标签
this.childList = this.$children;
//设置滑动距离。平分设备宽度
this.slideWidth = window.innerWidth/this.childList.length;
//设置状态线初始化滑动位置
this.$refs.line.style.width = this.slideWidth+"px";
},
methods:{
//切换tab触发事件
changeTab:(event,item,index)=>{
self.activeKey = item.actKey;
self.$refs.line.style.transform = "translateX("+self.slideWidth*index+"px)";
self.$refs.line.style.transition = "transform .3s";
self.$emit('on-click',item,index);//将切换tab的事件暴露给父组件
},
//初始化时tab状态设置与相应内容显示
updateNav:()=>{
self.$children.map((item,index)=>{
if(item.actKey == self.activeKey){
item.show = true;
self.$nextTick(function() {
self.$refs.line.style.transform = "translateX("+self.slideWidth*index+"px)";
self.$refs.line.style.transition = "transform 0s";
});
}else {
item.show = false;
}
})
}
},
watch: {
//监听当前tab,显示相应内容
activeKey() {
self.$children.map((item)=>{
if(item.actKey == self.activeKey){
item.show = true;
}else {
item.show = false;
}
})
}
}
}
</script>
<style>
.active-tab{
color:#158ef3;
height: 50px;
font-weight: bold;
line-height: 50px;
font-size: 16px;
}
.tab{
color:#333;
height: 50px;
line-height: 50px;
font-size: 16px;
}
.tabs{
display: flex;
justify-content: space-around;
align-items: center;
height: 50px;
border-bottom: 1px solid #f6f6f6;
}
.tab-line{
position: absolute;
left: 0;
border-bottom: 2px solid #158ef3;
height: 50px;
}
</style>
<template>
<div v-if="show">
<slot></slot>
</div>
</template>
<script>
export default {
name: "ElTabPanes",
data(){
return {
show: false //初始时将所有内容隐藏
}
},
props:{
actKey:{
type: String,
default: "1"
}, label:{
type: String,
default: ""
},
},
mounted(){
this.$parent.updateNav();
},
}
</script>
最后总结一下封装一个 tabs 的核心思路和方法。
1. 设置初始化 tab 标签
在 tab-pane 子组件中将所有的内容隐藏(show 属性设置为 false),在 tabs 父组件内接收由开发者自定义的 activeKey,定义一个方法,将 activeKey与子组件的 actKey 比较,如果相同,则该 tab 为初始化时激活的 tab 标签,将相应 tab 的 show 属性设置为 true,并修改 tab 样式。该方法在 tab-pane 子组件中调用。
将 tab-pane 子组件中所有的开发者添加的内容隐藏:
tabs 父组件提供的方法:
<div :class="[activeKey == item.actKey? 'active-tab' : 'tab']" @click="changeTab($event,item.actKey,index)" v-for="(item,index) in childList">{
{
item.label}}</div>
changeTab:(event,tab,index)=>{
self.activeKey = tab;
self.$refs.line.style.transform = "translateX("+self.slideWidth*index+"px)";
self.$refs.line.style.transition = "transform .3s";
self.$emit('on-click',event,tab)
},
<div ref="line" class="tab-line"></div>
.tab-line{
height: 2px;
background: #409eff;
position: absolute;
left: 0;
margin-top: 20px;
}
activeKey() {
self.$children.map((item)=>{
if(item.actKey == self.activeKey){
item.show = true;
}else {
item.show = false;
}
})
}