admin管理员组

文章数量:1022552

I'm working in a Vue ponent that I'll publish when it's finished that wraps Clusterize.js (there is a vue-clusterize ponent but it only works for v1.x). What I want to achieve is to render a huge list of items pretty fast using Vue. I actually need it for a table. I tried with vue-virtual-scroll but it doesn't support tables and the performance is not that good. So I wanted to try with Clusterize.js.

Because I want this ponent to be highly configurable I decided that you will be able to provide a scoped slot for each row of the items list where you will receive the item. The problem is when I try to assign the scoped slot from the clusterize ponets to each row before mounting the ponent it doesn't work.

Here you have some snippets of my code (it is just a mvp)

clusterize.vue

Template

<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>

Script

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

export default {
  name: 'Clusterize',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
    };
  },
  puted: {
    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
    };
  },
};

ponent.vue

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

The thing is that if it don't use a scoped slot it works perfectly but I really need to use them otherwise the ponent doesn't have any sense.

I'll appreciate any help or advice. Thank you so much in advance.

I'm working in a Vue ponent that I'll publish when it's finished that wraps Clusterize.js (there is a vue-clusterize ponent but it only works for v1.x). What I want to achieve is to render a huge list of items pretty fast using Vue. I actually need it for a table. I tried with vue-virtual-scroll but it doesn't support tables and the performance is not that good. So I wanted to try with Clusterize.js.

Because I want this ponent to be highly configurable I decided that you will be able to provide a scoped slot for each row of the items list where you will receive the item. The problem is when I try to assign the scoped slot from the clusterize ponets to each row before mounting the ponent it doesn't work.

Here you have some snippets of my code (it is just a mvp)

clusterize.vue

Template

<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>

Script

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

export default {
  name: 'Clusterize',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
    };
  },
  puted: {
    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
    };
  },
};

ponent.vue

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

The thing is that if it don't use a scoped slot it works perfectly but I really need to use them otherwise the ponent doesn't have any sense.

I'll appreciate any help or advice. Thank you so much in advance.

Share Improve this question asked Aug 9, 2018 at 16:24 andreszandresz 531 silver badge5 bronze badges 1
  • 1 Can you please provide a reproduction (jsfiddle)? – Marlon Barcarol Commented Aug 9, 2018 at 16:55
Add a ment  | 

1 Answer 1

Reset to default 2

The issue should be caused by mount different Vue instance to same el multiple times (please look into the second demo, you shouldn't mount multiple instances to same element, the following instances will not mount since the element is already “blocked” by first instance).

My solution: create Vue instance (doesn't bind to el) in the air then take vm.$el as the output.

Please look into below simple demo,

Vue.config.productionTip = false
Vue.ponent('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
    };
  },
  puted: {
    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./clusterize.js/0.18.0/clusterize.min.css" rel="stylesheet"/>
<script src="https://unpkg./[email protected]/dist/vue.js"></script>
<script src="https://cdn.bootcss./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>

Please look into below demo: created multiple Vue instance to same el, but Vue always uses first instance to render (I can't find any useful statement at Vue Guide, probably from the source codes from Vue Github we can find out the logic. If someone knows, please feel free to edit my answer or add a ment).

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./[email protected]/dist/vue.js"></script>
<script src="https://cdn.bootcss./clusterize.js/0.18.0/clusterize.min.js"></script>
<div id="app">
  <a>{{test}}</a>
</div>

I'm working in a Vue ponent that I'll publish when it's finished that wraps Clusterize.js (there is a vue-clusterize ponent but it only works for v1.x). What I want to achieve is to render a huge list of items pretty fast using Vue. I actually need it for a table. I tried with vue-virtual-scroll but it doesn't support tables and the performance is not that good. So I wanted to try with Clusterize.js.

Because I want this ponent to be highly configurable I decided that you will be able to provide a scoped slot for each row of the items list where you will receive the item. The problem is when I try to assign the scoped slot from the clusterize ponets to each row before mounting the ponent it doesn't work.

Here you have some snippets of my code (it is just a mvp)

clusterize.vue

Template

<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>

Script

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

export default {
  name: 'Clusterize',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
    };
  },
  puted: {
    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
    };
  },
};

ponent.vue

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

The thing is that if it don't use a scoped slot it works perfectly but I really need to use them otherwise the ponent doesn't have any sense.

I'll appreciate any help or advice. Thank you so much in advance.

I'm working in a Vue ponent that I'll publish when it's finished that wraps Clusterize.js (there is a vue-clusterize ponent but it only works for v1.x). What I want to achieve is to render a huge list of items pretty fast using Vue. I actually need it for a table. I tried with vue-virtual-scroll but it doesn't support tables and the performance is not that good. So I wanted to try with Clusterize.js.

Because I want this ponent to be highly configurable I decided that you will be able to provide a scoped slot for each row of the items list where you will receive the item. The problem is when I try to assign the scoped slot from the clusterize ponets to each row before mounting the ponent it doesn't work.

Here you have some snippets of my code (it is just a mvp)

clusterize.vue

Template

<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>

Script

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

export default {
  name: 'Clusterize',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
    };
  },
  puted: {
    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
    };
  },
};

ponent.vue

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

The thing is that if it don't use a scoped slot it works perfectly but I really need to use them otherwise the ponent doesn't have any sense.

I'll appreciate any help or advice. Thank you so much in advance.

Share Improve this question asked Aug 9, 2018 at 16:24 andreszandresz 531 silver badge5 bronze badges 1
  • 1 Can you please provide a reproduction (jsfiddle)? – Marlon Barcarol Commented Aug 9, 2018 at 16:55
Add a ment  | 

1 Answer 1

Reset to default 2

The issue should be caused by mount different Vue instance to same el multiple times (please look into the second demo, you shouldn't mount multiple instances to same element, the following instances will not mount since the element is already “blocked” by first instance).

My solution: create Vue instance (doesn't bind to el) in the air then take vm.$el as the output.

Please look into below simple demo,

Vue.config.productionTip = false
Vue.ponent('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
    };
  },
  puted: {
    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./clusterize.js/0.18.0/clusterize.min.css" rel="stylesheet"/>
<script src="https://unpkg./[email protected]/dist/vue.js"></script>
<script src="https://cdn.bootcss./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>

Please look into below demo: created multiple Vue instance to same el, but Vue always uses first instance to render (I can't find any useful statement at Vue Guide, probably from the source codes from Vue Github we can find out the logic. If someone knows, please feel free to edit my answer or add a ment).

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./[email protected]/dist/vue.js"></script>
<script src="https://cdn.bootcss./clusterize.js/0.18.0/clusterize.min.js"></script>
<div id="app">
  <a>{{test}}</a>
</div>

本文标签: javascriptVuejs scopedSlots don39t work for Vue instanceStack Overflow