Vue.js $ scopedSlots不适用于Vue实例

时间:2018-08-09 16:24:03

标签: javascript vue.js vuejs2 clusterize

我正在使用Vue组件,该组件将在包装Clusterize.js时完成发布(有一个vue集群组件,但仅适用于v1.x)。我想要实现的是使用Vue快速渲染大量项目。我实际上需要一张桌子。我尝试使用vue-virtual-scroll,但尝试使用doesn't support tables,但效果并不理想。所以我想尝试使用Clusterize.js。

因为我希望此组件具有高度可配置性,所以我决定您将能够为接收项目的项目列表的每一行提供一个限定范围的插槽。问题是,当我尝试在安装组件无效之前将集群化组件中的作用域插槽分配给每一行时。

这里有一些我的代码片段(这只是一个MVP)

clusterize.vue

模板

<div class="clusterize">
<table>
  <thead>
    <tr>
      <th>Headers</th>
    </tr>
  </thead>
</table>
<div
  ref="scroll"
  class="clusterize-scroll">
  <table>
    <tbody
      ref="content"
      class="clusterize-content">
      <tr class="clusterize-no-data">
        <td>Loading...</td>
      </tr>
    </tbody>
  </table>
</div>

脚本

import Vue from 'vue';
import Clusterize from 'clusterize.js';

export default {
  name: 'Clusterize',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
    };
  },
  computed: {
    rows() {
      return this.items.map(item => '<tr><slot :item="1"/></tr>');
    },
  },
  watch: {
    rows() {
      this.clusterize.update(this.rows);
    },
  },
  mounted() {
    const scrollElem = this.$refs.scroll;
    const contentElem = this.$refs.content;

    this.clusterize = new Clusterize({
      rows: this.rows,
      scrollElem,
      contentElem,
    });

    this.clusterize.html = (template) => {
      contentElem.innerHTML = template;
      const instance = new Vue({ el: contentElem });

      instance.$slots = this.$slots;
      instance.$scopedSlots = this.$scopedSlots;
      instance.$mount();

      console.log(instance.$scopedSlots); // empty
      console.log(instance.$slots) // not empty
    };
  },
};

component.vue

<clusterize :items="test">
  <template slot-scope="props">
    item
  </template>
</clusterize>

问题是,如果它不使用作用域限定的插槽,则可以完美工作,但是我真的需要使用它们,否则该组件将毫无意义。

我们将不胜感激。 提前非常感谢您。

1 个答案:

答案 0 :(得分:1)

该问题应该是由于将不同的Vue实例多次安装到同一el引起的(请查看第二个演示,您不应将多个实例安装到同一元素,{{ 3}})。

我的解决方案:在空中创建Vue实例(不绑定到el),然后将vm.$el作为输出。

请查看下面的简单演示

Vue.config.productionTip = false
Vue.component('clusterize', {
  template: `<div class="clusterize">
<table>
  <thead>
    <tr>
      <th>Headers</th>
    </tr>
  </thead>
</table>
<div
  ref="scroll"
  class="clusterize-scroll">
  <table>
    <tbody
      ref="content"
      id="clusterize-id"
      class="clusterize-content">
      <tr class="clusterize-no-data">
        <td>Loading...</td>
      </tr>
    </tbody>
  </table>
</div></div>`,
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
      clusterVueInstance: null
    };
  },
  computed: {
    rows() {
      return this.items.map(item => {
      	return '<tr><td><span>' +item+'</span><slot :item="1"/></td></tr>'
      });
    },
  },
  watch: {
    rows() {
      this.clusterize.update(this.rows);
    },
  },
  mounted() {
    const scrollElem = this.$refs.scroll;
    const contentElem = this.$refs.content;

    this.clusterize = new Clusterize({
      rows: this.rows,
      scrollElem,
      contentElem,
    });
		
    this.clusterize.html = (template) => {
      this.clusterize.content_elem.innerHTML = template;
      if(this.clusterVueInstance) {
				this.clusterVueInstance.$destroy()
        this.clusterVueInstance = null
      }
      
      this.clusterVueInstance = new Vue({  template: '<tbody>'+template+'</tbody>' })
      //or use Vue.extend()
      this.clusterVueInstance.$slots = this.$slots
      this.clusterVueInstance.$scopedSlots = this.$scopedSlots
      this.clusterVueInstance.$mount()
      this.clusterize.content_elem.innerHTML = this.clusterVueInstance.$el.innerHTML
      //console.log(this.clusterVueInstance.$scopedSlots); // empty
      //console.log(this.clusterVueInstance.$slots) // not empty*/
    };
  }
})

app = new Vue({
  el: "#app",
  data() {
    return {
      test: ['Puss In Boots', 'test 1', 'test2'],
      index: 0
    }
  },
  mounted: function () {
  	//this.test = ['Puss In Boots', 'test 1', 'test2']
  },
  methods: {
    addItem: function () {
      this.test.push(`test ` + this.index++)
    }
  }
})
<link href="https://cdn.bootcss.com/clusterize.js/0.18.0/clusterize.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/clusterize.js/0.18.0/clusterize.min.js"></script>
<div id="app">
  <button @click="addItem()">
  Add Item
  </button>
  <clusterize :items="test">
  <template slot-scope="props">
    item: {{props.item}}
  </template>
  </clusterize>
</div>

请查看下面的演示:在同一个el上创建了多个Vue实例,但是Vue始终使用第一个实例进行渲染(我可能在Vue Guide中找不到任何有用的声明从Vue Github的源代码中我们可以找到逻辑。如果有人知道,请随时编辑我的答案或添加评论。

Vue.config.productionTip = false
app1 = new Vue({
  el: '#app',
  data () {
    return {
    test: 'test 1'
    }
  },
  mounted(){
    console.log('app1', this.test)
  }
})

app2 = new Vue({
  el: '#app',
  data () {
    return {
    test: 'test 2'
    }
  },
  mounted(){
    console.log('app2', this.test)
  }
})
//app1.$data.test = 3
//app1.$mount() //manual mount
app2.$data.test = 4
app2.$mount() //manual mount
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/clusterize.js/0.18.0/clusterize.min.js"></script>
<div id="app">
  <a>{{test}}</a>
</div>