最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue render函數(shù)實戰(zhàn)之實現(xiàn)tabs選項卡組件

 更新時間:2019年04月22日 11:42:01   作者:heath_learning  
這篇文章主要介紹了Vue render函數(shù)實戰(zhàn)之實現(xiàn)tabs選項卡組件的相關(guān)知識,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下

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

1、實現(xiàn)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="定時任務(wù)補(bǔ)償" name="fourth">定時任務(wù)補(bǔ)償</el-tab-pane>
</el-tabs>

問題:

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

2、實現(xiàn)思路

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

3、代碼實現(xiàn)

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);
  }
  },
  // 標(biāo)簽頁點擊事件
  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內(nèi)容。這里只接收<PTabPane>組件,因為我們需要根據(jù)<PTabPane>組件的數(shù)量來生成
   * <PTabNav>組件,如果參差了其它節(jié)點則會導(dǎo)致不能正確生成<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更新的時候強(qiáng)制更新父組件,以觸發(fā)PTabNav才能更新
  this.$parent.$forceUpdate();
  }
 },
 mounted() {
  // 當(dāng)當(dāng)前組件創(chuàng)建的時候?qū)?dāng)前組件添加到父組件的pTabPanes中,以觸發(fā)PTabNav才能更新
  this.$parent.addPane(this);
 },
 destroyed() {
  if(this.$el && this.$el.parentNode){
  this.$el.parentNode.removeChild(this.$el);
  }
  // 當(dāng)當(dāng)前組件銷毀時需從父組件中的pTabPanes中移除當(dāng)前組件,以觸發(fā)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結(jié)構(gòu)
 <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">技術(shù)教學(xué)</div>
   <div class="p-tabs_nav-item">新手教學(xué)</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
  }
  });
  // 生成標(biāo)簽頁
  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="定時任務(wù)補(bǔ)償" name="fourth">定時任務(wù)補(bǔ)償</PTabPane>
</PTabs>

總結(jié)

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

相關(guān)文章

  • vue?ant?design?封裝彈窗表單的使用

    vue?ant?design?封裝彈窗表單的使用

    這篇文章主要介紹了vue?ant?design?封裝彈窗表單的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Vue實戰(zhàn)之項目開發(fā)時常見的幾個錯誤匯總

    Vue實戰(zhàn)之項目開發(fā)時常見的幾個錯誤匯總

    vue作為前端主流的3大框架之一,目前在國內(nèi)有著非常廣泛的應(yīng)用,下面這篇文章主要給大家介紹了關(guān)于Vue實戰(zhàn)之項目開發(fā)時常見的幾個錯誤匯總的相關(guān)資料,對大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-03-03
  • Vue實現(xiàn)push數(shù)組并刪除的例子

    Vue實現(xiàn)push數(shù)組并刪除的例子

    今天小編就為大家分享一篇Vue實現(xiàn)push數(shù)組并刪除的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • vue插槽slot的理解和使用方法

    vue插槽slot的理解和使用方法

    這篇文章主要給大家介紹了關(guān)于vue中插槽slot的理解和使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 小白教程|一小時上手最流行的前端框架vue(推薦)

    小白教程|一小時上手最流行的前端框架vue(推薦)

    這篇文章主要介紹了前端框架vue,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 在Vue3中安全訪問子組件的示例代碼

    在Vue3中安全訪問子組件的示例代碼

    在?Vue?開發(fā)中,父子組件間的通信是高頻場景,當(dāng)需要在父組件中直接調(diào)用子組件的方法時,模板引用(Template?Refs)是最直接的解決方案,本文將通過一段?Vue?3?代碼片段,深入剖析如何通過?TypeScript?實現(xiàn)類型安全的子組件實例訪問,需要的朋友可以參考下
    2025-03-03
  • vue彈窗消息組件的使用方法

    vue彈窗消息組件的使用方法

    這篇文章主要為大家詳細(xì)介紹了vue彈窗消息組件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • 從Vuex中取出數(shù)組賦值給新的數(shù)組,新數(shù)組push時報錯的解決方法

    從Vuex中取出數(shù)組賦值給新的數(shù)組,新數(shù)組push時報錯的解決方法

    今天小編就為大家分享一篇從Vuex中取出數(shù)組賦值給新的數(shù)組,新數(shù)組push時報錯的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue 每次渲染完頁面后div的滾動條保持在最底部的方法

    vue 每次渲染完頁面后div的滾動條保持在最底部的方法

    下面小編就為大家分享一篇vue 每次渲染完頁面后div的滾動條保持在最底部的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • 解決vue中this.$set()不更新問題

    解決vue中this.$set()不更新問題

    我在做一個附件刪除功能的時候發(fā)現(xiàn),明明打印出來附件已經(jīng)沒有數(shù)據(jù)了但是頁面就是不刷新, this.$set()不生效,所以本文小編給大家介紹了vue中this.$set()不更新的解決方法,需要的朋友可以參考下
    2023-11-11

最新評論

乐至县| 聂荣县| 两当县| 河源市| 连江县| 嵊泗县| 洪江市| 福海县| 昌宁县| 昭平县| 千阳县| 三亚市| 渭源县| 封开县| 鲁甸县| 鹰潭市| 廊坊市| 梨树县| 宿松县| 贡山| 南岸区| 仲巴县| 昌乐县| 枣强县| 华阴市| 泰顺县| 南康市| 丰原市| 汤阴县| 荔波县| 永丰县| 湟源县| 应城市| 安吉县| 色达县| 中山市| 资阳市| 温州市| 衡山县| 保德县| 玛多县|