资讯专栏INFORMATION COLUMN

第十一集: 从零开始实现一套pc端vue的ui组件库( tab切换组件 )

VEIGHTZ / 3244人阅读

摘要:第十一集从零开始实现切换组件本集定位我们先来聊聊切换的意义不管是手机还是屏幕的大小是有限的人眼睛看到的范围也是有限的人们看信息的时候并不喜欢跳转这种操作或是我们要查某个知识点进入网站之后看了几眼没有需要的相关信息也就理所当然的退出去继续搜索

第十一集: 从零开始实现( tab切换组件 )

本集定位:
我们先来聊聊 tab 切换的意义, 不管是手机还是pc, 屏幕的大小是有限的, 人眼睛看到的范围也是有限的, 人们看信息的时候并不喜欢"跳转"这种操作, 或是我们要查某个知识点, 进入网站之后, 看了几眼没有需要的相关信息也就理所当然的退出去继续搜索了, 而有时某些我们想要的知识点可能在网站的底部, 但人们是有浏览习惯的, 这就需要在第一眼看到的区域里面, 尽可能多的展示"关键词"与"关键信息", tab正是解决了如何"扩大"有限的空间这一问题.

tab组件与其他组件不同, 他需要至少两个组件来配合完成功能,写三个组件使用起来很讨人厌, 只写一个组件, 不管是语义化还是书写方式上都太差了, 参考element的设计本次我们也是采用的双组件,编写上他与单一的组件不同的地方就是, 它涉及到两个组件之间的通讯问题.

1:需求分析

两部分组成, 上部是标题的展示, 下部根据选中状态进行展示内容

标题要有明确的激活状态

为了性能, 内容展示不可以使用v-if

像这种包裹型的组件, 不允许干扰用户的任何操作, 比如不可以有.stop修饰符

使用方法应如下

我以cc-tab为包裹组件的父级标签
cc-tab-pane为每一个展示内容的标签

 
      1号的内容
      2号的内容
      3号的内容
 

预期效果:

2:基础的搭建

vue-cc-ui/src/components/Tab/index.js

import Tab from "./main/tab.vue"
import TabPane from "./main/tab-pane.vue"

Tab.install = function(Vue) {
  Vue.component(Tab.name, Tab);
  Vue.component(TabPane.name, TabPane);
};

export default Tab

容器组件
vue-cc-ui/src/components/Tab/main/tab.vue

vue-cc-ui/src/components/Tab/main/tab-pane.vue
只负责展示与提供组件的参数给容器


容器组件他还要接收参数

label 也就是tab显示的标签名 (给用户看的)

name 也就是当点击时, 此标签的id (给开发用的)

这两个分开设置还有一个原因, 就是label可以是重复的, 因为他不是唯一标识, name不可重复

props: {
    label: {
      type: String,
      required: true
    },
    name: {
      type: String,
      required: true
    }
  },
3:基础功能

一. 我们先把导航功能做出来, 让标题显示出来
在父级的容器里面:

// 个人比较推荐的代码规范
// mounted 与 created 这种钩子, 放在最底部
// 因为他 不会经常变动, 他只是负责启动代码
// 他要符合单一职责, 不允许有具体的逻辑判断
// 他启动的函数, 如果有关初始化的, 必须以"init"作为开头
mounted() {
    this.initNav();
  }

initNav

initNav() {
// 仅负责对每一项的处理
      this.layer(item => {
        let result = {
          label: item.label,
          name: item.name,
          icon: item.icon
        };
        // 放入我们的导航数组里面
        this.navList.push(result);
      });
    },
    // 原理与map, reduce, 这类函数一样, 
    // 每一步操作 都会吐给用户
    layer(task) {
      this.$slots.default.map(item => task(item.componentInstance));
    }

解释一下:

this.$slots : 得到这个父级容器内的所有插槽元素的一个对象, 例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到, default 属性包括了所有没有被包含在具名插槽中的节点,或 v-slot:default 的内容。

上面循环this.$slots.default 获取到的每一个item就是"节点元素",为什么打上"", 因为这个节点是被vue处理过的, 并不是传统意义上的节点;

componentOptions: 顾名思义,这个组件的一些配置项, 比如listeners未接收的事件, tag标签名, propsData, 而propsData里面包含了我们需要的name 以及 label, 但是他需要 componentOptions.propsData.name才可以取到值.

componentInstance: 组件状态, 其身上有组件的this上面的参数 可以直接获取到 props传入的值, 比如componentInstance.name 就会取到传入的name, 上面为什么选他? 就是因为他只要"."一次就可以取到值了, 程序员的本性

上面我们得到了一个用户传入子组件的配置汇总, 我们可以循环展示他

  • // 像这种内容的展示, 写上标签代码布局上更舒服

handClick, 点击事件负责把用户的操作给父级看, 毕竟我们绑定了v-model所以给个input事件,
tab-click是用户接受的事件

 handClick(e, name) {
      this.$emit("input", name);
      this.$emit("tab-click", e);
      // 这里的更改选择项需要用 宏任务, 否则测试的时候有显示不正确的bug
      setTimeout(() => this.initSeleced(), 0);
    },

initSeleced 一个专门做选择的方法

// 一句话的事
 initSeleced() {
    // 利用我们之前定义好的循环函数
    // item就是每一个子组件, 这些子组件数据是映射的, 所以可以进行修改
    // 当子组件的value与激活的name相同时, 组件的展示被激活
      this.layer(item => (item.showItem = item.name == this.value));
    },

子组件




现在我们把核心功能写完了, 但不要忘记小小的细节.
初始化选择

  mounted() {
    this.initNav();
    // 初始阶段也要激活一下用户选择tab栏
    this.initSeleced();
  }
4: 样式的设计

完善样式, 比如tab的激活状态, 激活动画

tab的不同样式, 不同风格

icon的添加

/vue-cc-ui/src/style/Tab.scss

@import "./common/var.scss";
@import "./common/mixin.scss";
@import "./common/extend.scss";

@include b(tab) {
    @include brother(nav) {
    // 整体的title布局就是不换行的横向布局
        display: flex;
        flex-wrap: nowrap;
        text-align: center;
        // 提供一条浅色的横线
        border-bottom: 1px solid #eee;
        margin-bottom: 10px;
        &>li {
        // 主要就是每一个标签的样式
            cursor: pointer;
            display: flex;
            position: relative;
            align-items: center;
            border-bottom: none;
            background-color: white;
            padding: 10px 20px;
            transition: all 0.2s;
            &:hover {
            // 给个有好的反馈
                transform: scale(0.8)
            };
                &::after {
                // 这个就是下面的选中横线, 平时缩放为0, 使用的时候再出现
                    content: "";
                    position: absolute;
                    left: 6px;
                    bottom: 0;
                    right: 6px;
                    transform: scale(0);
                    transition: all 0.2s;
                }
            @include when(active) {
            // 被激活的时候, 会字体变色, 会浮现出横线
                color: $--color-nomal;
                &::after {
                    border-bottom: 2px solid $--color-nomal;
                    transform: scale(1);
                }
            }
        }
    }
}

添加icon

// 我就简写了
  • // 传入name就出现, 否则不出现
  • 其他的类型的tab, 把标签包裹起来

    效果图:


    允许用户选择找这种样式

      相关样式也要兼容

      @include when(card) {
                  &::after {
                      display: none
                  }
                  &>li {
                      border-bottom: none;
                      border: 1px solid #eee;
                      &:hover {
                          transform: scale(1)
                      }
                  };
                  &>li+li {
                      border-left: none
                  };
                  &>.is-active {
                      border-bottom: none;
                      &::after {
                          content: "";
                          position: absolute;
                          border-bottom: 2px solid white;
                          left: 0;
                          right: 0;
                          bottom: -1px;
                      }
                  };
                  &>:nth-last-child(1) {
                      border-top-right-radius: 7px;
                  };
                  &>:nth-child(1) {
                      border-top-left-radius: 7px;
                  };
              }

      上面的写法有个技巧就是下面这段
      用户有可能只有一个tab, 你可能会问, 只有一个干么要做tab?? 我只能说, 怎么玩是你的事, 我只负责实现.
      所以在只有一项的时候, 就不能只弯曲他的左上角, 还要让他的右上角也是有弧度的

      // 这两个选择器完美解决了问题
      // 只有一个的时候, 它既是第一个也是最后一个
      &>:nth-last-child(1) {
          border-top-right-radius: 7px;
      };
      &>:nth-child(1) {
          border-top-left-radius: 7px;
      };

      至此tab的功能已经做完, 总的来说这个tab组件算是cc-ui组件中比较好写的一个了.

      end
      大家继续一起学习,一起进步, 早日实现自我价值!!
      下一集准备聊聊"评分组件", 也就是选择小星星的那个, 做起来很有意思的组件,我挺喜欢的.

      本套ui的github地址:github
      个人技术博客: 链接

      文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

      转载请注明本文地址:https://www.ucloud.cn/yun/106650.html

      相关文章

      • 一集: 从零开始实现一套pcvueui组件(环境搭建)

        摘要:第一集从零开始实现环境的搭建工程定位本套工程定位在端针对的组件库名字的由来是我从年养到现在的一直大金毛是我的吉祥物原因本人上一份工作参与了大型的保险公司后台管理系统的搭建对的端框架有过一定的了解感受到了他们真的很强大同时也存在少许的不足其实 第一集: 从零开始实现(环境的搭建) 工程定位: 本套工程, 定位在pc端针对vue的ui组件库 名字的由来 cc是我从2015年养到现在的...

        Ashin 评论0 收藏0
      • 第十三集: 从零开始实现一套pcvueui组件( 评分组件 小星星 )

        摘要:第十三集从零开始实现一套端的组件库评分组件小星星本集定位说起评分的话最早看到这种形式是电影网站每部电影得到几颗星这种方式后来就出现了用户来手动选星星打分的玩法这些方式更直观更吸引用户参与进去这个组件其实还有很多玩法比加载动画我可以把星星不断 第十三集: 从零开始实现一套pc端vue的ui组件库( 评分组件 小星星 ) 1. 本集定位      说起评分的话, 最早看到这种形式是电影网站...

        jerryloveemily 评论0 收藏0
      • 第十集: 从零开始实现一套pcvueui组件( 计数器组件 )

        摘要:第十集从零开始实现计数器组件本集定位听到计数器这个名字很多人是不是一瞬间没有什么印象毕竟这个组件用的比较少就是那种左边一个右边一个控制某些数量的时候才会用到比如我之前做的商城小程序只有下单页面的规格弹出框里面才有他的身影如果是涉及到处理商 第十集: 从零开始实现( 计数器组件 ) 本集定位: 听到计数器这个名字很多人是不是一瞬间没有什么印象, 毕竟这个组件用的比较少,就是那种左边...

        Sanchi 评论0 收藏0
      • 第十集: 从零开始实现一套pcvueui组件( 计数器组件 )

        摘要:第十集从零开始实现计数器组件本集定位听到计数器这个名字很多人是不是一瞬间没有什么印象毕竟这个组件用的比较少就是那种左边一个右边一个控制某些数量的时候才会用到比如我之前做的商城小程序只有下单页面的规格弹出框里面才有他的身影如果是涉及到处理商 第十集: 从零开始实现( 计数器组件 ) 本集定位: 听到计数器这个名字很多人是不是一瞬间没有什么印象, 毕竟这个组件用的比较少,就是那种左边...

        Kerr1Gan 评论0 收藏0

      发表评论

      0条评论

      最新活动
      阅读需要支付1元查看
      <