首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >vue 实现tab切换

vue 实现tab切换

作者头像
切图仔
发布2022-09-08 16:49:50
发布2022-09-08 16:49:50
2.8K0
举报
文章被收录于专栏:生如夏花绚烂生如夏花绚烂

我们要实现如下效果

单看效果似乎很简单,实则不然 首先我们的tab一般是这样的结构

代码语言:javascript
复制
<tabs>
   <tab label="one">
         //内容区
        <div>
           ....
        </div>
   <tab>
</tabs>

tabs是整个tab选项的容器,每个tab代表一个切换项,tab提供插槽用于展现当前tab的内容 似乎没有什么问题

但是我们之前写原生js就知道html渲染的tab是这样的

代码语言:javascript
复制
<ul>
    <li>one</li>
    <li>two</li>
    <li>three</li>
<ul>
<div>
    this is content 
</div>

tab标签与tab内容是分离的,我们不可能这样做

代码语言:javascript
复制
<li>
    one
    <div> this is content</div>
<li>

但是我们的组件确是如上的结构,这种方式应该怎么做,vue的组件也是从上到下执行的,我们不可能将其分离出来,通过本文你会知道如何去实现这样一个tab切换

新键tab主容器组件(tabs) /tab/tabs.vue

代码语言:javascript
复制
<script>
    export default {
        name :"Tabs",
        props:{
            //通过value控制显示哪个tab
            value:{
                type:[String,Number],
                required:true
            }
        },
        render(){
            return (
                <div class="tabs">
                    <ul class="tabs-header">
                        {this.$slots.default}
                    </ul>
                </div>
            )
        },
        data(){
        },
        methods:{
        }
    }
</script>
<style lang="stylus" scoped>
.tabs-header
  display flex
  list-style none
  margin 0
  padding 0
  border-bottom 2px solid #ededed
</style>

这里我们使用了render函数和jsx语法使我们的组件更加灵活,ul提供默认插槽放置标签

新键tab组件 /tab/tab.vue

代码语言:javascript
复制
<script>
export default {
    name:"Tab",
    props:{
        index:{
            required:true,
            type:[Number,String]
        },
        label:{
            type:String,
            default:'tab'
        }
    },
    computed:{
        active(){
            return false
        }
    },
    methods:{
        handleClick(){
        }
    },
    render(){
        //插槽 或者label
        const tab =  this.$slots.label || <span>{this.label}</span>
        const classNames = {
            tab:true,
            active:this.active
        }
        return  (
            <li class={classNames} on-click={this.handleClick}>
                {tab}
            </li>
        )
    }
}
</script>
<style lang="stylus" scoped>
.tab
  list-style none
  line-height 40px
  margin-right 30px
  position relative
  bottom -2px
  cursor pointer
  &.active
    border-bottom 2px solid blue
  &:last-child
    margin-right 0
</style>

该组件接收index和lable用于与父组件配合控制其激活状态lable设置标签文字,在render函数中我们展示标签内容,要不以插槽的方式要么以传值的方式

接下来我们在全局注册这两个组件 /tab/index.js

代码语言:javascript
复制
import Tabs from './tabs.vue'
import Tab from './tab.vue'

export default (Vue)=>{
   Vue.component(Tabs.name,Tabs)
   Vue.component(Tab.name,Tab)
}

在入口文件引入

代码语言:javascript
复制
...
import Tabs from './components/tabs/index.js'
Vue.use(Tabs) //tab组件

...

接下来我们可以在全局引用

代码语言:javascript
复制
<div class="tab-container">
    <tabs value="1">
       <tab label="tabs" :index="1"></tab>
       <tab  index="2">
            <span slot="label">two</span>
       </tab>
        <tab label="tabs3" index="3">
             <span>three</span>
         </tab>
        </tabs>
</div>

基本效果搭建完成,接下来我们来完成tab切换

首先我们先让标签之间能够切换 给tabs添加点击事件,当元素被点击的时候我们向外emit一个change事件,并将当前被点击的tab索引暴露出去

tabs.vue

代码语言:javascript
复制
...
 methods:{
            onChange(index){

                this.$emit('change',index)
            }
        }
    }
</script>
<style lang="stylus" scoped>
.tabs-header
  display flex
  list-style none
  margin 0
  padding 0
  border-bottom 2px solid #ededed
</style>

我们在外面监听change事件,并改变tabs的value值

代码语言:javascript
复制
...
 <tabs :value="tabValue" @change="handleChangeTab">
     <tab label="tabs" :index="1">

data(){
     return {
            tabValue:1
        }
},
handleChangeTab(value){
       this.tabValue = value
 }
...

写到这里我们还没有触发 onChange方法 我们在标签被点击的时候触发父元素的onChange方法 并将当前索引暴露出去

代码语言:javascript
复制
 computed:{
        active(){
            // return false
            //判断如果父元素的vaule与当前索引相同则为激活状态
            return this.$parent.value === this.index
        }
    },
    methods:{
        handleClick(){
            this.$parent.onChange(this.index)
        }
    },

我们使元素处于激活状态的依据是标签索引与父元素value相同,当标签被点击时,我们会将当前索引暴露出去,同时时父元素的value等于当前被点击标签索引,这样即实现标签的选中激活。

接下来我们开发标签对应的内容展示部分 我们在填入与当前标签相关的内容是这样操作的

代码语言:javascript
复制
 <tab  index="2">
     <span slot="label">two</span>
     <span>two</span>
  </tab>

如果这样做,他被渲染成html是这样的

代码语言:javascript
复制
<li>
two
<span>two</span>
</li>

这里以span举例不太恰当,但实际我们不会这样做,这样做也不合理,那怎么使我们组件以最简单的方式书写,而每次渲染的时候都是如下形式呢

代码语言:javascript
复制
<ul>
    <li>one</li>
    <li>two</li>
    <li>three</li>
<ul>
<div>
    this is content 
</div>

将标签组件的$solts.label抽离出来即可 首先我们在父容器定义panes接收标签组件 tabs.vue

代码语言:javascript
复制
...
data(){
    return  {
         panes:[]
    }

},
...

标签组件将自己添加到父组件panes数组 tab.vue

代码语言:javascript
复制
mounted(){
     this.$parent.panes.push(this)
},

tabs.vue遍历子元素拿到当前激活状态的tab,并进行展示

代码语言:javascript
复制
render(){
     const contents = this.panes.map(pane=>{
         return pane.active ? pane.$slots.default:null
     })//获取当前激活状态的tab内容
     return (
         <div class="tabs">
             <ul class="tabs-header">
                {this.$slots.default}
             </ul>
             <div class="tab-container">
                   {contents}
              </div>
          </div>
      )
}

我们在进行全局调用

代码语言:javascript
复制
  <div class="tab-container">
           <tabs :value="tabValue" @change="handleChangeTab">
            <tab label="tabs" :index="1">
                <span>one</span>
            </tab>
            <tab  index="2">
            <span slot="label">two</span>
             <span>two</span>
            </tab>

            <tab label="tabs3" index="3">
                 <span>three</span>
            </tab>
        </tabs>
 </div>

到此基本完成

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档