<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答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
        主站蜘蛛池模板: 亚洲国产一区二区三区| 欧美男同gv免费网站观看| 无码国产亚洲日韩国精品视频一区二区三区 | 91麻豆国产自产在线观看亚洲| 亚洲Aⅴ在线无码播放毛片一线天 亚洲avav天堂av在线网毛片 | 精品亚洲国产成人| 免费视频爱爱太爽了| 亚洲精品国产情侣av在线| 久久aⅴ免费观看| 午夜亚洲AV日韩AV无码大全| 日本亚洲欧洲免费天堂午夜看片女人员 | 免费看a级黄色片| 天天爽亚洲中文字幕| 成年女人男人免费视频播放| 涩涩色中文综合亚洲| 四虎www成人影院免费观看| 亚洲熟女乱色一区二区三区| 成人au免费视频影院| 亚洲精品宾馆在线精品酒店| 国产又大又粗又硬又长免费| 特级毛片免费播放| 亚洲免费观看视频| 18禁亚洲深夜福利人口| 亚洲女初尝黑人巨高清| 亚洲国产精品一区二区三区久久 | 免费看国产精品3a黄的视频| 84pao强力永久免费高清| 69视频免费在线观看| 蜜桃精品免费久久久久影院| 亚洲国产精品成人AV无码久久综合影院 | 国产精品免费观看久久| 国产精品麻豆免费版| 亚洲男人的天堂在线va拉文| 亚洲AV日韩AV鸥美在线观看| 亚洲一区免费视频| 精品亚洲福利一区二区| 在线播放免费人成毛片乱码| 97无码免费人妻超级碰碰碰碰| 亚洲中文字幕在线观看| 宅男666在线永久免费观看| 美女被免费喷白浆视频|