本集定位:
咱們先來聊聊 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
你們繼續一塊兒學習,一塊兒進步, 早日實現自我價值!!
下一集準備聊聊'評分組件', 也就是選擇小星星的那個, 作起來頗有意思的組件,我挺喜歡的.