githup地址:https://github.com/dubuxunqi/vue-tabs
1.首先写index.html文件

 <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>选项卡</title>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        <div id="app" v-cloak>
            <!-- @input接收子组件传来的name -->
            <tabs v-model="value" @input="tabData">
                <pane label="标签一" name="1">
                    标签一的内容
                </pane>
                <pane label="标签二" name="2">
                    标签二的内容
                </pane>
                <pane label="标签三" name="3">
                    标签三的内容
                </pane>
            </tabs>
        </div>
        <script type="text/javascript" src="vue.js"></script>
        <script type="text/javascript" src="pane.js"></script>
        <script type="text/javascript" src="tabs.js"></script>
        <script type="text/javascript">
            var app = new Vue({
                el:"#app",
                data:{
                    // 初始显示第几个
                    value:"3"
                },
                methods:{
                    tabData:function(item){
                        console.log(item);
                    }
                }
            })
        </script>
    </body>
    </html>

2.我们需要两个组件,tabs组件和pane组件,pane组件嵌套在tabs组件里面,pane组件中slot内放业务逻辑代码(代码里面都有详细的注释,这里就不在一一解释)
首先看下pane组件内容:

 Vue.component('pane',{
          name:"pane",
          template:'\
                <div class="pane" v-show="show">\
                <slot></slot>\
                </div>',
          props:{
                // 用来标识点击的哪个标签
                name:{
                      type:String
                },
                // 标签的标题内容
                label:{
                      type:String,
                      default:""
                }
          },
          data:function(){
                // show控制显示哪个内容
                return {
                      show:true
                }
          },
          methods:{
                // this.$parent访问父组件中的方法,注意在业务代码中尽量不要使用$parent来操作父链,这种方法试用于标签这样独立的组件
                updateNav(){
                      this.$parent.updateNave();
                }
          },
          watch:{
                // 监听label,label变化时,调用updateNav
                label(){
                      this.updateNav();
                }
          },
          mounted(){
                // mounted生命周期钩子同样调用updateNav
                this.updateNav();
          }
    })
tabs组件的内容:
Vue.component('tabs',{
    template:'\
        <div class="tabs">\
            <div class="tabs-bar">\
             <!--标题页的标题 v-for遍历, :class 动态绑定class-->\
                <div \
                    :class="tabCls(item)"\
                    v-for="(item,index) in navList"\
                    @click="handleChange(index)">\
                    {{item.label}}\
                </div>\
            </div>\
            <div class="tabs-content">\
                <!-- 这里的slot是嵌套pane组件 -->\
                <slot></slot>\
            </div>\
        </div>',
    props:{
        value:{
            //接收父组件的value
            type:[String]
        }
    },
    data:function(){
        return {
            //保存父组件的value到currentValue变量中,以便在本地维护
            currentValue:this.value,
            //将pane的标题保存到数组中
            navList:[]
        }
    },
    methods:{
        tabCls:function(item){
            //为当前选中的tab加一个tabs-tab-active class
            return [
                'tabs-tab',
                {
                    'tabs-tab-active':item.name === this.currentValue
                }
            ];
        },
        getTabs:function(){
            //使用$children遍历子组件,得到所有的pane组件
            return this.$children.filter(function(item){
                return item.$options.name === 'pane';
            });
        },
        //更新tabs
        updateNave:function(){
            this.navList = [];
            // foreach里面的回调里的this不再是tabs组件本身,所以设置_this=this
            var _this= this;
            this.getTabs().forEach(function(pane,index){
                _this.navList.push({
                    label:pane.label,
                    name:pane.name || index
                });
                //如果没有设置name,默认设置为索引值
                if(!pane.name) pane.name = index;
                //设置第一个pane为当前显示的tab
                if(index === 0){
                    if(!_this.currentValue){
                        _this.currentValue = pane.name || index;
                    }
                }
            });
            this.updateStatus();     
        },
        updateStatus:function(){
            var tabs = this.getTabs();
            var _this = this;
            //显示当前选中的tab对应的pane组件,隐藏没有选中的
            tabs.forEach(function(tab){
                return tab.show = tab.name ===_this.currentValue;
            });
        },
         //点击tab标题触发
        handleChange:function(index){
            var nav = this.navList[index];
            var name = nav.name;
             //改变当前选中的tab,触发watch
            this.currentValue = name;
            //实现子组件与父组件通信
            this.$emit('input',name);
        }
    },
    watch:{
        // 监听value变化
        value:function(val){
            this.currentValue = val;
        },
        // 监听currentValue变化,更新对应的pane组件
        currentValue:function(){
            this.updateStatus();
        }
    }
})

有什么不明白的,欢迎评论,我会解答,或是来拍砖,我们一起学习。

Logo

前往低代码交流专区

更多推荐