本集定位:
咱们先来聊聊 tab 切换的意义, 不论是手机仍是pc, 屏幕的大小是有限的, 人眼睛看到的范围也是有限的, 人们看信息的时候并不喜欢'跳转'这种操做, 或是咱们要查某个知识点, 进入网站以后, 看了几眼没有须要的相关信息也就理所固然的退出去继续搜索了, 而有时某些咱们想要的知识点可能在网站的底部, 但人们是有浏览习惯的, 这就须要在第一眼看到的区域里面, 尽量多的展现'关键词'与'关键信息', tab正是解决了如何'扩大'有限的空间这一问题. css
tab组件与其余组件不一样, 他须要至少两个组件来配合完成功能,写三个组件使用起来很讨人厌, 只写一个组件, 不论是语义化仍是书写方式上都太差了, 参考element的设计本次咱们也是采用的双组件,编写上他与单一的组件不一样的地方就是, 它涉及到两个组件之间的通信问题.vue
使用方法应以下git
我以cc-tab为包裹组件的父级标签
cc-tab-pane为每个展现内容的标签程序员
<cc-tab v-model="activeName"> <cc-tab-pane label="1号" name="one">1号的内容</cc-tab-pane> <cc-tab-pane label="2号" name="two">2号的内容</cc-tab-pane> <cc-tab-pane label="3号" name="three">3号的内容</cc-tab-pane> </cc-tab>
预期效果:github
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函数
<template> <div class="cc-tab" > // 毕竟会不少标签, ul li的语义化固然是最好的; // 好比3个标题, 你用3个div, 可是使用ul li 就要4个标签, 优缺点都是有的. <ul class="cc-tab-nav" > <li v-for="item in navList" > 标签名 </li> </ul> // 这里展现内容 <slot /> </div> </template>
vue-cc-ui/src/components/Tab/main/tab-pane.vue
只负责展现与提供组件的参数给容器布局
<template> <div> // 展现的内容咱们直接写在标签里面, 因此slot就够了 <slot></slot> </div> </template>
容器组件他还要接收参数性能
这两个分开设置还有一个缘由, 就是label能够是重复的, 由于他不是惟一标识, name不可重复学习
props: { label: { type: String, required: true }, name: { type: String, required: true } },
一. 咱们先把导航功能作出来, 让标题显示出来
在父级的容器里面:
// 我的比较推荐的代码规范 // 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)); }
解释一下:
上面咱们获得了一个用户传入子组件的配置汇总, 咱们能够循环展现他
<div class="cc-tab"> <ul class="cc-tab-nav"> <li v-for="item in navList" :key='item.name' // 当 :class="{ 'is-active':item.name === value }" // 这个点击事件就要通知子组件, 到底显示谁 @click="handClick($event,item.name)" > // 像这种内容的展现, 写上标签代码布局上更舒服 <template> // 展现他的标签名 {{item.label}} </template> </li> </ul> <slot /> </div>
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)); },
子组件
<template> // 毕竟用户反复切换tab的可能性是存在的, show的效率更高一些 <div v-show="showItem"> <slot></slot> </div> </template> <script> export default { name: "ccTabPane", props: { label: { type: String, required: true }, name: { type: String, required: true }, icon: { type: String } }, data() { return { // 默认固然是false, 不显示 showItem:false }; } }; </script>
如今咱们把核心功能写完了, 但不要忘记小小的细节.
初始化选择
mounted() { this.initNav(); // 初始阶段也要激活一下用户选择tab栏 this.initSeleced(); }
/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
// 我就简写了 <li v-for="item in navList" :key='item.name' :class="{ 'is-active':item.name === value }" @click="handClick($event,item.name)" > // 传入name就出现, 不然不出现 <ccIcon v-if="item.icon" :name='item.icon' // 有一个被激活的颜色 // 这里还能够这么写 (item.name === value)||'#409EFF' // 可是三元这里比较灵活, 之后可能会改变默认颜色 :color="item.name === value?'#409EFF':''" /> <template> {{item.label}} </template> </li>
其余的类型的tab, 把标签包裹起来
效果图:
容许用户选择找这种样式
<ul class="cc-tab-nav" :class="{ 'is-card':type=='card' }" >
相关样式也要兼容
@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
你们继续一块儿学习,一块儿进步, 早日实现自我价值!!
下一集准备聊聊'评分组件', 也就是选择小星星的那个, 作起来颇有意思的组件,我挺喜欢的.