• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當前位置: 首頁 - 科技 - 知識百科 - 正文

    Vue render函數實戰之實現tabs選項卡組件

    來源:懂視網 責編:小采 時間:2020-11-27 21:58:19
    文檔

    Vue render函數實戰之實現tabs選項卡組件

    Vue render函數實戰之實現tabs選項卡組件:用過Element ui庫的童鞋肯定知道<el-tabs>組件,簡單、好用、可以自定義標簽頁,不知道廣大童鞋們在剛開始使用<el-tabs>組件的時候有沒有想過它是如何實現的?我咋剛開始使用<el-tabs>組件的時候就有去想過,也想去實現一個超級簡單的t
    推薦度:
    導讀Vue render函數實戰之實現tabs選項卡組件:用過Element ui庫的童鞋肯定知道<el-tabs>組件,簡單、好用、可以自定義標簽頁,不知道廣大童鞋們在剛開始使用<el-tabs>組件的時候有沒有想過它是如何實現的?我咋剛開始使用<el-tabs>組件的時候就有去想過,也想去實現一個超級簡單的t

    用過Element ui庫的童鞋肯定知道<el-tabs>組件,簡單、好用、可以自定義標簽頁,不知道廣大童鞋們在剛開始使用<el-tabs>組件的時候有沒有想過它是如何實現的?我咋剛開始使用<el-tabs>組件的時候就有去想過,也想去實現一個超級簡單的tabs選項卡組件,無奈當時功力不夠,未能實現。最近的一個簡單項目中正好要用到選項卡組件,由于項目簡單也就沒有使用任何第三方庫,于是就自己動手寫了個選項卡組件。

    1、實現tabs選項卡組件的思考

    <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>

    問題:

    1. 如何根據<el-tab-pane>來生成標簽頁?
    2. 如何過濾<el-tabs>組件中的子元素,使得在使用的時候只顯示<el-tab-pane>,而不會顯示其他組件或div之類的元素?

    2、實現思路

    想根據<el-tab-pane>來生成標簽頁就需要使用到<slot>,使用<slot>用<template>的形式肯定是不行的,因為無法獲取到<slot>的數量;使用<template>的形式行不通,那就只有使用render函數了
    過濾<el-tabs>組件中的子元素也需要使用render函數

    3、代碼實現

    index.js

    import PTabs from './PTabs';
    import PTabPane from './PTabPane';
    
    export default function tabsInstall(Vue) {
     if(tabsInstall.installed){
     return;
     }
     Vue.component('PTabs', PTabs);
     Vue.component('PTabPane', PTabPane);
    }

    PTabs.vue

    <script>
     import PTabNav from './PTabNav';
     export default {
     name: "PTabs",
     props: {
     value: {
     type: [String, Number],
     default: ''
     },
     beforeClick: {
     type: Function,
     default(){
     return function () {};
     }
     }
     },
     components: {
     PTabNav
     },
     data(){
     return {
     pTabPanes: [],
     currentName: this.value || 0
     }
     },
     methods: {
     addPane(pane){
     this.pTabPanes.push(pane);
     if(!this.currentName){
     this.setCurrentName(this.pTabPanes[0].name);
     }
     },
     removePane(pane){
     let index = this.pTabPanes.indexOf(pane);
     if(index > -1){
     this.pTabPanes.splice(index, 1);
     }
     },
     setCurrentName(name){
     if(this.currentName !== name){
     this.currentName = name;
     this.$emit('input', name);
     }
     },
     // 標簽頁點擊事件
     handTabNavClick(name, pane, e){
     if(this.currentName === name || pane.disabled){
     return;
     }
     let before = this.beforeClick();
     if(before && before.then){
     before.then(() => {
     this.setCurrentName(name);
     this.$emit('tabClick', pane, e);
     })
     }else{
     this.setCurrentName(name);
     this.$emit('tabClick', pane, e);
     }
     }
     },
     watch: {
     value(newVal){
     this.setCurrentName(newVal);
     },
     currentName(){
     this.$nextTick(() => {
     this.$refs.p_tab_nav.scrollToActiveTab();
     });
     }
     },
     render(h) {
     let {$scopedSlots} = this;
     let $default = $scopedSlots.default();
     let qTabPanes = $default.map(item => {
     /* 過濾<PTabs>xxx</PTabs>中傳遞的xxx內容。這里只接收<PTabPane>組件,因為我們需要根據<PTabPane>組件的數量來生成
     * <PTabNav>組件,如果參差了其它節點則會導致不能正確生成<PTabNav>組件 */
     if(item.componentOptions && item.componentOptions.tag === 'PTabPane'){
     return item;
     }
     });
     let qTab = h('PTabNav', {
     props: {
     // 將tab-pane傳遞給 <PTabNav>組件,<PTabNav>組件就知道要有多少個tab-item了
     tabPanes: this.pTabPanes,
     handTabNavClick: this.handTabNavClick
     },
     ref: 'p_tab_nav'
     });
     let qTabBody = h('div', {
     staticClass: 'p-tabs_content'
     }, qTabPanes);
    
     console.log($default)
     return h('div', {
     staticClass: 'p-tabs'
     }, [qTab, qTabBody]);
     },
     mounted() {
     //console.log(this)
     this.$nextTick(() => {
     this.$refs.p_tab_nav.scrollToActiveTab();
     });
     }
     }
    </script>
    <style lang="stylus">
    .p-tabs{
     .p-tabs_header{
     position: relative;
     margin-bottom: 15px;
     &.is-scrollable{
     padding-left: 20px;
     padding-right: 20px;
     }
     }
     .p-tabs_nav-prev,
     .p-tabs_nav-next{
     position: absolute;
     top: 0;
     width: 20px;
     height: 100%;
     display: none;
     &::before{
     position: absolute;
     content: ' ';
     font-size: 0;
     line-height: 0;
     width: 10px;
     height: 10px;
     top: 50%;
     left: 50%;
     border-top: 1px solid #eee;
     border-left: 1px solid #eee;
     margin: -5px 0 0 -5px;
     }
     cursor: pointer;
     &.disabled{
     cursor: default;
     border-color: #aaa;
     }
     }
     .p-tabs_nav-prev{
     left: 0;
     &:before{
     transform: rotate(-45deg);
     }
     }
     .p-tabs_nav-next{
     right: 0;
     &:before{
     transform: rotate(135deg);
     }
     }
     .p-tabs_header{
     &.is-scrollable{
     .p-tabs_nav-prev,
     .p-tabs_nav-next{
     display: block;
     }
     }
     }
     .p-tabs_nav-scroll{
     overflow: hidden;
     }
     .p-tabs_nav-list{
     position: relative;
     float: left;
     white-space: nowrap;
     transition: transform .3s;
     }
     .p-tabs_nav-item{
     display: inline-block;
     height: 40px;
     line-height: 40px;
     padding: 0 20px;
     color: #fff;
     cursor: pointer;
     &.active,
     &:hover{
     color: #ffb845;
     }
     &.disabled{
     cursor: not-allowed;
     color: #aaa;
     &:hover{
     color: #aaa;
     }
     }
     }
     .p-tabs_content{
     position: relative;
     overflow: hidden;
     }
     .p-tabs-pane{
     color: #fff;
     }
    }
    </style>

    PTabPane.vue

    <template>
     <div class="p-tabs-pane" v-show="show">
     <slot></slot>
     </div>
    </template>
    <script>
     export default {
     name: "PTabPane",
     props: {
     label: {
     type: String,
     default: ''
     },
     name: {
     type: [String, Number],
     default: ''
     },
     disabled: {
     type: Boolean,
     default: false
     }
     },
     data(){
     return {
     loaded: false
     }
     },
     computed: {
     show(){
     if(this.$parent.currentName === this.name){
     if(!this.loaded){
     this.loaded = true;
     }
     return true;
     }
     return false;
     }
     },
     watch: {
     label(){
     // label更新的時候強制更新父組件,以觸發PTabNav才能更新
     this.$parent.$forceUpdate();
     }
     },
     mounted() {
     // 當當前組件創建的時候將當前組件添加到父組件的pTabPanes中,以觸發PTabNav才能更新
     this.$parent.addPane(this);
     },
     destroyed() {
     if(this.$el && this.$el.parentNode){
     this.$el.parentNode.removeChild(this.$el);
     }
     // 當當前組件銷毀時需從父組件中的pTabPanes中移除當前組件,以觸發PTabNav才能更新
     this.$parent.removePane(this);
     }
     }
    </script>

    PTabNav.vue

    <script>
     function noop() {};
    
     export default {
     name: "PTabNav",
     props: {
     tabPanes: {
     type: Array,
     default(){
     return [];
     }
     },
     handTabNavClick: {
     type: Function,
     default(){
     return function () {};
     }
     }
     },
     data(){
     return {
     navPrevDisabled: true,
     navNextDisabled: true,
     // 控制左右箭頭顯示
     scrollable: false,
     listOffset: 0
     }
     },
     methods: {
     navPrevClickEvent(){
     if(!this.navPrevDisabled){
     let navScrollW = this.$refs.nav_scroll.offsetWidth;
     let navListW = this.$refs.nav_list.offsetWidth;
     let maxTransformX = 0;
     let transformX = this.listOffset - navScrollW;
     if(transformX < maxTransformX){
     transformX = maxTransformX;
     }
     if(transformX === this.listOffset){
     return;
     }
     console.log('上一頁按鈕點擊了', transformX);
     this.listOffset = transformX;
     if(transformX === 0){
     this.navPrevDisabled = true;
     this.navNextDisabled = false;
     }else if(transformX === (navListW - navScrollW)){
     this.navPrevDisabled = false;
     this.navNextDisabled = true;
     }else{
     this.navPrevDisabled = false;
     this.navNextDisabled = false;
     }
     }
     },
     navNextClickEvent(){
     if(!this.navNextDisabled){
     let navScrollW = this.$refs.nav_scroll.offsetWidth;
     let navListW = this.$refs.nav_list.offsetWidth;
     let maxTransformX = navListW - navScrollW;
     let transformX = this.listOffset + navScrollW;
     if(transformX > maxTransformX){
     transformX = maxTransformX;
     }
     if(transformX === this.listOffset){
     return;
     }
     console.log('下一頁按鈕點擊了', transformX);
     this.listOffset = transformX;
     if(transformX === 0){
     this.navPrevDisabled = true;
     this.navNextDisabled = false;
     }else if(transformX === (navListW - navScrollW)){
     this.navPrevDisabled = false;
     this.navNextDisabled = true;
     }else{
     this.navPrevDisabled = false;
     this.navNextDisabled = false;
     }
     }
     },
     // 計算 .p-tabs_nav-list 是否溢出
     calculateListSpilled(){
     let navScrollW = this.$refs.nav_scroll.offsetWidth;
     let navListW = this.$refs.nav_list.offsetWidth;
     if(navScrollW < navListW){
     this.scrollable = true;
     }else{
     if(this.listOffset > 0){
     this.listOffset = 0;
     }
     this.scrollable = false;
     }
     },
     // 滾動條滾動到激活的tab
     scrollToActiveTab(){
     if(this.scrollable){
     this.$nextTick(() => {
     let navScrollW = this.$refs.nav_scroll.offsetWidth;
     let navList = this.$refs.nav_list;
     let activeTab = navList.querySelector('.active');
     let activeTabOffsetLeft = 0;
     if(activeTab){
     activeTabOffsetLeft = activeTab.offsetLeft;
     }
    
     let transformX = activeTabOffsetLeft + activeTab.offsetWidth - navScrollW;
    
     transformX = transformX < 0 ? 0 : transformX;
     this.listOffset = transformX;
     if(transformX === 0){
     this.navPrevDisabled = true;
     this.navNextDisabled = false;
     }else if(transformX === (navList.offsetWidth - navScrollW)){
     this.navPrevDisabled = false;
     this.navNextDisabled = true;
     }else{
     this.navPrevDisabled = false;
     this.navNextDisabled = false;
     }
     });
     }
     }
     },
     computed: {
     listOffsetTran(){
     console.log('dddd',`translateX(-${this.listOffset}px);`)
     return {
     transform: `translateX(-${this.listOffset}px)`
     }
     }
     },
     render(h) {
     /*dom結構
     <div class="p-tabs_header is-scrollable">
     <span class="p-tabs_nav-prev disabled"></span>
     <span class="p-tabs_nav-next"></span>
     <div class="p-tabs_nav-scroll">
     <div class="p-tabs_nav-list">
     <div class="p-tabs_nav-item active">全部</div>
     <div class="p-tabs_nav-item disabled">技術教學</div>
     <div class="p-tabs_nav-item">新手教學</div>
     </div>
     </div>
     </div>
     */
     let navPrev = h('span', {
     staticClass: 'p-tabs_nav-prev',
     'class': {
     disabled: this.navPrevDisabled
     },
     on: {
     click: this.navPrevClickEvent
     }
     });
     let navNext = h('span', {
     staticClass: 'p-tabs_nav-next',
     'class': {
     disabled: this.navNextDisabled
     },
     on: {
     click: this.navNextClickEvent
     }
     });
     // 生成標簽頁
     let navItems = this.tabPanes.map(item => {
     let $labelSlot = item.$scopedSlots.label ? item.$scopedSlots.label() : null;
     let labelContent = $labelSlot ? $labelSlot : item.label;
     return h('div', {
     staticClass: 'p-tabs_nav-item',
     'class': {
     active: this.$parent.currentName === item.name,
     disabled: item.disabled,
     },
     on: {
     click: (e) => {
     this.handTabNavClick(item.name, item, e);
     }
     }
     }, [labelContent]);
     });
     let navScroll = h('div', {
     staticClass: 'p-tabs_nav-scroll',
     ref: 'nav_scroll'
     }, [
     h('div', {
     staticClass: 'p-tabs_nav-list',
     ref: 'nav_list',
     style: this.listOffsetTran
     }, [navItems])
     ]);
    
     return h('div', {
     staticClass: 'p-tabs_header',
     'class': {
     'is-scrollable': this.scrollable
     },
     }, [navPrev, navNext, navScroll]);
     },
     updated(){
     this.calculateListSpilled();
     },
     mounted() {
     this.calculateListSpilled();
     }
     }
    </script>

    4、使用

    main.js

    // 引入tabs組件
    import tabs from './components/p-tabs';
    // 全局注冊p-tabs組件
    Vue.use(tabs);

    頁面中使用

    <PTabs v-model="activeName">
     <PTabPane label="用戶管理" name="first">用戶管理</PTabPane>
     <PTabPane label="配置管理" name="second">配置管理</PTabPane>
     <PTabPane label="角色管理" name="third">角色管理</PTabPane>
     <PTabPane label="定時任務補償" name="fourth">定時任務補償</PTabPane>
    </PTabs>

    總結

    以上所述是小編給大家介紹的Vue render函數實戰之實現tabs選項卡組件,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
    如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    Vue render函數實戰之實現tabs選項卡組件

    Vue render函數實戰之實現tabs選項卡組件:用過Element ui庫的童鞋肯定知道<el-tabs>組件,簡單、好用、可以自定義標簽頁,不知道廣大童鞋們在剛開始使用<el-tabs>組件的時候有沒有想過它是如何實現的?我咋剛開始使用<el-tabs>組件的時候就有去想過,也想去實現一個超級簡單的t
    推薦度:
    標簽: VUE 組件 tabs
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 91精品一区二区综合在线| 精品四虎免费观看国产高清午夜| 97国产精品视频| 精品熟女少妇av免费久久| 国产精品成人精品久久久| 成人精品一区二区三区| 国产精品无码素人福利不卡| 久草热8精品视频在线观看| 亚洲国产精品成人网址天堂| 日韩人妻精品无码一区二区三区| 久久夜色精品国产欧美乱| 国产精品福利在线播放| 亚洲精品免费观看| 欧美精品VIDEOSEX极品| 午夜精品久久久久久毛片| 精品欧美一区二区在线看片| 日本欧美韩国日本精品| 欧美在线精品一区二区三区 | 国语自产精品视频在线区| 亚洲处破女AV日韩精品| 91老司机深夜福利精品视频在线观看| 中文字幕无码精品三级在线电影 | 国产精品亚洲视频| 99riav国产精品| 欧美一区二区精品系列在线观看| 国产精品免费大片| 国产欧美精品一区二区色综合 | 国产精品后入内射日本在线观看| 一夲道无码人妻精品一区二区| 午夜三级国产精品理论三级 | 亚洲视频在线精品| 国产在线精品网址你懂的| 久久精品www人人爽人人| 日韩一区精品视频一区二区| 亚洲精品无码精品mV在线观看| 亚洲午夜福利精品无码| 中文字幕无码精品亚洲资源网久久 | 一本大道久久a久久精品综合 | 91精品欧美综合在线观看| 91精品国产自产在线观看永久| 国产精品成人观看视频国产|