Skip to content
On this page

使用vue 实现tabs 的总结

TIP

关于我为啥会实现一遍tabs组件呢?因为我们产品想在切换tabs的时候加入一些动画效果。那再参考Element UI 实现Tabs的一些总结 以及反思。

基础用法:

vue
<template>
  <el-tabs v-model="activeName" @tab-click="handleClick">
    <el-tab-pane label="用户管理" name="first">用户管理</el-tab-pane>
    <el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
    <el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
    <el-tab-pane label="定时任务补偿" name="fourth">定时任务补偿</el-tab-pane>
  </el-tabs>
</template>
<script>
  export default {
    data() {
      return {
        activeName: 'second'
      };
    },
    methods: {
      handleClick(tab, event) {
        console.log(tab, event);
      }
    }
  };
</script>

WARNING

<el-tabs/> 嵌套 <el-tab-pane/>, 那咱们思考下 怎么做到使用activeName做到控制那个tab页显示隐藏呢?怎么做到读取<el-tab-pane/>的传值去绘制 tab页头呢?

读取全部<el-tab-pane/>传参 做tab页表头

TIP

<el-tabs/>组件里面使用 this.$slots.default获取全部组件做过滤,具体相关代码如下

vue
<!-- tabs.vue -->
<template>
  <div class="tabs-container">
    <tab-nav
      :panes="tabPanes"
      :current-name="currentName"
      :on-tab-nav="onTabNav"
    ></tab-nav>

    <div class="tab-body">
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Tabs',

  computed: {
    tabPanes() {
      if(this.$slots.default) {
        // 使用 this.$slots.default  or this.$children 都能获取到子组件数据信息
        return this.$slots.default
        .filter(vnode => vnode.tag &&
            vnode.componentOptions && 
            vnode.componentOptions.Ctor.options.name === 'ElTabPane');
      }
      return []
    },
  },
}
</script>

<el-tab-pane/> 怎么做到根据el-tabs v-model显示隐藏的?

TIP

简单说下v-model,在组件上使用v-model数据双向绑定 实际是向组件传入了 :value='绑定的值', 组件需要更新数据使用this.$emit('input', 新数据),这么交互的。

那么在el-tabs组件中

vue
<!-- tabs.vue -->
<script>
export default {
 props: {
    value: { // v-model 数据
      type: String,
      required: true,
      default: '',
    },
  },
}
</script>

在 el-tab-pane 组件中

vue
<!-- tab-pane.vue -->

<template>
  <div
    v-show="active"
    v-if="((!lazy || loaded) || active)"
    class="tab-pane__item"
  >
    <slot></slot>
  </div>
</template>
<script>
export default {
  name: 'TabPane',

  props: {
    label: {
      type: String,
      required: true,
      default: '',
    },
    name: {
      type: String,
      required: true,
      default: '',
    },
    lazy: {
      type: Boolean,
      required: false,
      default: false,
    },
  },

  data() {
    return {
      loaded: false,
    };
  },

  computed: {
    active() {
      // 使用 this.$parent 获取 el-tabs 组件 v-model 绑定的value
      const active = this.$parent.value === (this.name || this.index);
      if (active) {
        this.loaded = true;
      }
      return active;
    },
  },
}
</script>

动画设计参考

TIP

1、animate 一个特别好用的轻量级动画库
2、Effeckt.css 集成了各种组件炫酷动画
3、minimamente 各种炫酷交互式设计

Released under the MIT License.