使用自定义方法构建可重用组件

时间:2016-11-11 01:10:21

标签: vue.js

我正在尝试使用vuejs构建可重用的选项卡组件。我还在学习vue的一些基本概念,很难完成制表和切换逻辑。现在可以在组件本身的选项卡之间切换。但是我让我的组件听外部触发器有一些问题。

现在,我可以在$refs的帮助下将我的标签切换到组件之外。但是当我试图让它可重复使用时,这种方法听起来并不实用。我该怎么办?

以下是JSFiddle



Vue.component('tabs', {
  template: `
  <div class="tabs">
    <div class="tab-titles">
          <a :class="{'active':tab.active}" v-for="tab in tablist" href="#" class="tab-title" @click.prevent="activateTab(tab)">{{tab.title}}</a>
      </div>
      <div class="tab-contents">
          <slot></slot>
      </div>
  </div>    
  `,
  data() {
    return {
      tablist: [],
    }
  },

  methods: {

    activateTab: function(tab) {
      this.tablist.forEach(t => {
        t.active = false;
        t.tab.is_active = false
      });

      tab.active = true;
      tab.tab.is_active = true;
    },

    activateTabIndex: function(index) {
      this.activateTab(this.tablist[index]);
    },

    collectTabData: function(tabData) {
      this.tablist.push(tabData)
    }
  },

});

//==============================================================================

Vue.component('tab', {
  template: `
  <div :class="{'active':is_active}" class="tab-content">
    <slot></slot>
  </div>
  `,

  data() {
    return {
      is_active: this.active
    }
  },

  mounted() {
    this.$parent.collectTabData({
      tab: this,
      title: this.title,
      active: this.active,
      is_active: this.is_active
    });
  },

  props: {
    title: {
      type: String,
      required: true
    },
    active: {
      type: [Boolean, String],
      default: false
    },
  }

});

//==============================================================================

Vue.component('app', {
  template: `
  <div class="container">

    <tabs ref="foo">

      <tab title="tab-title-1">
        <h3>content-1</h3>
        Initial content here
      </tab>

      <tab title="tab-title-2" active>
        <h3>content-2</h3>
        Some content here
      </tab>

      <tab title="tab-title-3">
        <h3>content-3</h3>
        Another content here
      </tab>

    </tabs>

    <a href="#" @click='switchTab(0)'>switch to tab(index:0)</a>

    </div>
  `,

  methods: {
    switchTab: function () {
      vm.$children[0].$refs.foo.activateTabIndex(0);
    }
  },

});

//==============================================================================

const vm = new Vue({
  el: '#inner-body',
});
&#13;
@import url('https://fonts.googleapis.com/css?family=Lato');

#inner-body{
 font-family: 'Lato', sans-serif; 
 background-color:#ffffff;
 padding:20px;
}

.tab-titles {

}

.tab-title {
  display: inline-block;
  margin-right: 10px;
  color: #bbb;
  text-decoration: none;
}

.tab-title.active {
  color: #06cd92;
  border-bottom:1px solid #06cd92;
}

.tab-contents{
    border: 1px solid #ddd;
    border-width:1px 0;
    margin-top:-1px;
    margin-bottom:20px;
}

.tab-content {
  display: none;
  padding-bottom:20px;
}

.tab-content.active {
  display: block;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.5/vue.min.js"></script>
<div id="inner-body">
  <app></app>
</div>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:2)

Vue组件应该通过Props单向传达给孩子(只有父母会改变孩子们的道具,而不是孩子们自己),孩子们通过发出事件与父母交流。使嵌套组件更容易推理,并正确地分离组件。那么当父母想要更改标签时你会怎么做?让我带您完成一个过程:

1)想象一下,我们在标签组件中添加了一个名为activeTab的道具(我在这里没有直接关注您的问题中的代码,只是松散地将其展示给它来展示这个过程更容易)。父母将随时改变该道具的价值。标签组件(在本例中也称为子组件)不应更改activeTab道具的值。相反,在标签组件内,为此道具添加观察者

子组件中的

(即标签)

    props: {

      /**
       * Prop parent can use to set active tab
       */
      'activeTab': {
        type: Number
      }
    },

    watch: {

      /**
       * Watch for changes on the activeTab prop.
       *
       * @param {boolean} val The new tab value.
       * @param {boolean} oldVal The old tab value.
       * @return {void}
       */
      activeTab: function (val, oldVal) {
        console.log('new: %s, old: %s', val, oldVal)
        // do something here to make the tabs change
        // and never alter the prop itself.
        this.switchTab(val)
      }
    },

2)现在,在您的父级上,您应该拥有一个反应数据属性,如果您愿意,可以将其命名为与您的道具相同:

父组件中的

(即app)

    data: {
      activeTab: 0
    }

3)然后我们需要将它改为上面改变数据属性的位置,支柱也会被改变。这是标签组件标签上的样子:

父组件中的

(即app)

    <tabs :activeTab="activeTab" ...

4)如果您希望标签组件有时也改变活动标签,您会怎么做?简单,只需在更改活动选项卡时发出事件:

子组件中的

(即标签)

  methods: {
    switchTab (newActiveTabValue) {

      // store the new active tab in some data property in this component, but not the "activeTab" prop, as mentioned earlier.
      this.whatever = newActiveTabValue

      // ... now emit the event
      this.$emit('switched', newActiveTabValue)

  }

5)您的父级现在应该侦听此发出的事件并更新自己的数据属性:

父组件中的

(即app)

    <tabs :activeTab="activeTab" @switched="activeTab = arguments[0]" ...

似乎需要更多的努力,但随着您的应用程序在复杂性方面的增长以及更多的东西变得嵌套,这一切都值得。您可以在官方文档here中阅读有关撰写组件的更多信息。