Vuejs:在插槽上使用keep-alive(或类似的东西)

时间:2017-05-24 19:07:26

标签: vue.js

我在vue中有树层次结构和制表符的组合。到目前为止,我已经或多或少地开始工作了。

我需要在关闭时完全删除dom中的内容,因为我所谈论的数据足够大,如果它只是留在dom中的display:none,它就会让浏览器瘫痪

看一下这个例子:

Vue.component('tabs', {
  template: '#tabs',
  data(){
    return {
      tabs: [],
      expanded:true,
      defaultExpanded:true,
      activeTab: null,
      hasChildren:false,
    };
  },
  methods: {
    toggle() {
      this.expanded = !this.expanded;
    },
    activate(tab) {
      if (this.activeTab) {
        this.activeTab.active = false;
      }
      tab.active = true;
      this.activeTab = tab;
    },
  },
  mounted(){
    for (i = 0; i < this.$slots.default.length; i++) {
      let t = this.$slots.default[i];
      if (t.componentOptions && t.componentOptions.tag == 'tab') {
        this.tabs.push(t.componentInstance);
      }
    }
    if (this.tabs.length) {
      this.activeTab = this.tabs[0];
      this.activeTab.active = true;
    }
    this.expanded = this.defaultExpanded;
  },
}); 

Vue.component('tab', {
  template: '#tab',
  data() {
    return {
      active: false,
    };
  },
  props: ['label'],
});

app = new Vue({
  'el': '#inst',
}); 
<!-- templates -->
<script type="text/x-template" id="tabs">
  <div @click.stop="toggle">
    <h1><slot name="h" /></h1>
    <div v-show="expanded" class="children">
        <ul><li v-for="tab in tabs" @click.stop="activate(tab)">{{tab.label}}</li></ul>
      <div style="border:1px solid #F00"><slot /></div>
    </div>
</script>
<script type="text/x-template" id="tab">
  <strong v-show="active"><slot /></strong>
</script>

<!-- data -->
<tabs id="inst">
  <div slot="h">Woot</div>
  <tab label="label">
    <tabs>
      <div slot="h">Weet</div>
      <tab label="sub">Weetley</tab>
    </tabs>
  </tab>
  <tab label="label2">Woot3</tab>
</tabs>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script> 

这样可以正常使用,但是如果我将v-show更改为v-if以获得性能,则会丢失状态,制表符按钮会停止显示 - 基本上会有很多内容中断。

问题是,只要我将v-if添加到tab模板的插槽中,整个组件在关闭时就会被删除。这意味着父组件的tabs列表是一组完全不同的对象,而不是第二次打开时显示的对象。

这意味着我无法单击标签来打开选项卡,因为在我到达选项卡时,选项卡将是不同的实例,并且每次关闭并打开父选项时,所有选项卡都将默认关闭。

我真正需要的是类似<keep-alive>的东西 - 我可以告诉vue将组件保留在内存中而不将它们呈现给dom。但是,当我补充说,整个事情都停止了。它似乎真的不适用于插槽,只适用于单个组件。

因此。 tl; dr:在使用v-if保持dom光线时,如何保持混合树和标签的状态?

1 个答案:

答案 0 :(得分:2)

Bert Evans' codepen的基础上,我创建了一个只是一个插槽的组件。我创建了一个keep-alive包装的动态组件,is插槽组件处于活动状态时,空白组件处于活动状态时。现在没有v-if,当您关闭并重新打开父项时,子项中会保留状态。

&#13;
&#13;
console.clear();

Vue.component('keepableSlot', {
  template: '#keepable-slot'
});

Vue.component('tabs', {
  template: '#tabs',
  data() {
    return {
      tabs: [],
      expanded: true,
      activeTab: null,
    };
  },
  methods: {
    addTab(tab) {
      this.tabs.push(tab)
    },
    toggle() {
      this.expanded = !this.expanded;
    },
    activate(tab) {
      if (this.activeTab) {
        this.activeTab.active = false;
      }
      tab.active = true;
      this.activeTab = tab;
    },
  },
  watch: {
    expanded(newValue) {
      console.log(this.$el, "expanded=", newValue);
    }
  }
});

Vue.component('tab', {
  props: ["label"],
  template: '#tab',
  data() {
    return {
      active: false
    }
  },
  created() {
    this.$parent.$parent.addTab(this)
  }
});

app = new Vue({
  'el': '#inst',
});
&#13;
.clickable-tab {
  background-color: cyan;
  border-radius: 5px;
  margin: 2px 0;
  padding: 5px;
}

.toggler {
  background-color: lightgray;
  border-radius: 5px;
  margin: 2px 0;
  padding: 5px;
}
&#13;
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<script type="text/x-template" id="tabs">
  <div>
    <h1 class="toggler" @click.stop="toggle">
      <slot name="h"></slot>
      (expanded={{expanded}})
    </h1>
    <keep-alive>
      <component :is="expanded && 'keepableSlot'">
        <div class="children">
          <ul>
            <li class="clickable-tab" v-for="tab in tabs" @click.stop="activate(tab)">{{tab.label}}</li>
          </ul>
          <div>
            <slot></slot>
          </div>
        </div>
      </component>
    </keep-alive>
  </div>
</script>

<script type="text/x-template" id="keepable-slot">
  <div>
    <slot></slot>
  </div>
</script>

<script type="text/x-template" id="tab">
  <strong>
    <component :is="active && 'keepableSlot'"><slot></slot></component>
  </div>
</script>

<!-- data -->
<tabs id="inst">
  <div slot="h">Woot</div>
  <tab label="label">
    <tabs>
      <div slot="h">Weet</div>
      <tab label="sub">Weetley</tab>
    </tabs>
  </tab>
  <tab label="label2">Woot3</tab>
</tabs>
&#13;
&#13;
&#13;