vue--组件-实用技能(2)

219 阅读1分钟

tab标签控件为例

step1:总控件

tabs.vue

<script>
  import TabContainer from './tab-container.vue'

  export default {
    name: 'Tabs',
    components: {
      TabContainer
    },
    /*provide() {
      return {
        value: this.value              //非reactive, 可用define方法解决
      }
    },*/
    props: {
      value: {
        type: [String, Number],
        required: true
      }
    },
    data() {
      return {
        panes: []
      }
    },
    render() {
      return (
        <div class="tabs">
          <ul class="tabs-header">                            //tab插入插槽中
            {this.$slots.default }
          </ul>
          <tab-container panes={this.panes}></tab-container>  //下container展示内容
        </div>
      )
    },
    methods: {
      onChange(index) {
        this.$emit('change', index)
      }
    },
  }
</script>

<style lang="stylus" scoped>
  .tabs-header
    display flex
    list-style none
    margin 0
    padding 0
    border-bottom 2px solid #ededed
</style>

step2:上部控件

tab.vue

<script>
  export default {
    name: 'Tab',
    props: {
      index: {
        required: true,
        type: [Number, String]
      },
      label: {
        type: String,
        default: 'tab'
      }
    },
    //inject: ['value'],
    mounted() {
      this.$parent.panes.push(this)
    },
    computed: {
      active() {
        //console.log("active is invoked ===",index)
        return this.$parent.value === this.index
        //return this.value === this.index  //组件套用的时候用provider
      }
    },
    methods: {
      handleClick() {
        this.$parent.onChange(this.index)//d调用上层方法
      }
    },
    render() {
      const tab = this.$slots.label || <span>{this.label}</span>
      const classNames = {
        tab: true,
        active: this.active
      }
      return (
        <li class ={classNames} on-click={this.handleClick}>
            {tab}
        </li>
      )
    }
  }
</script>
<style lang="stylus" scoped>
  .tab
    list-style none
    line-height 40px
    margin-right 30px
    position relative
    bottom -2px
    cursor pointer
    &.active
      border-bottom 2px solid black
    &:last-child
      margin-right 0
</style>

step3:内容控件

tab-container.vue

<script>
  export default {
    props: {
      panes: {
        type: Array,
        required: true
      }
    },
    render() {
      const contents = this.panes.map(pane => {
        return pane.active ? pane.$slots.default : null
      })
      return (
        <div class="tab-content">
          {contents}
        </div>
      )
    }
  }
</script>

step4:挂载到全局Vue上

index.js

import Tabs from './tabs.vue'
import Tab from './tab.vue'

export default (Vue) => {
  Vue.component(Tabs.name, Tabs)
  Vue.component(Tab.name, Tab)
}

————--------------——show time again,baby!!!———---------------—————

todu.vue

<template>
  <section class="real-app">
    <div class="tab-container">
      <tabs :value="filter" @change="handleChangeTab">        //直接引用,定制显示数据
        <tab :label="tab" :index="tab" v-for="tab in stats" :key="tab"/>
      </tabs>
    </div>
......

data() {
  return {
    stats: ['all', 'active', 'completed'],
    filter: 'all',
    todos: [],
  }
},

采坑记录: