在渲染函数中构建动态数量的 Vue 插槽

Build a dynamic number of Vue slots within a Render function

我正在尝试从渲染函数构建自定义组件。

正在呈现的这个组件接受任意数量的插槽。在下面的示例中,有三个可用插槽(名为 element_1element_2element_3)。

下面的Array.reduce()相当于:

scopedSlots: {
  "element_1": () => createElement('div', 'hello world'),
  "element_2": () => createElement('div', 'hello world'),
  "element_3": () => createElement('div', 'hello world'),
}

这是一个精简的例子 Array.reduce():

const records = [
  {
    "index": 1,
  },
  {
    "index": 2,
  },
  {
    "index": 3,
  }
]

render: function (createElement) {
  return createElement("replicator-component", {
    attrs: { elements: records.length},

    scopedSlots: records.reduce((a,x) => ({...a, 
      ['element_' + x.index]: 
      () => { createElement( 'div', 'hello world') }}), {})
  });
},

但是没有任何渲染,也没有任何错误可以指导我。有什么想法吗?

该 reduce 方法不起作用,因为它在 createElement('div', 'hello world'):

之前缺少 return

完整示例

const ReplicatorComponent = {

  template: `
 <div>
    <h1>replicator-component</h1>
    
    <slot name='element_1'></slot>
    <slot name='element_2'></slot>
    <slot name='element_3'></slot>
 </div>
`
}

const records = [{
    "index": 1,
  },
  {
    "index": 2,
  },
  {
    "index": 3,
  }
]



Vue.component('my-component', {
  render: function(createElement) {

    let slotContent = records.reduce((a, x) => ({ ...a,
      ['element_' + x.index]:
        () => {
        return  createElement('div', 'hello world')
        }
    }), {})
    return createElement(ReplicatorComponent, {
      attrs: {
        elements: records.length
      },
      scopedSlots: slotContent
    });
  },
})

var app = new Vue({
  el: '#app',

  data: () => ({})
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>

<div id="app">
  test

  <my-component></my-component>
</div>

我认为作用域插槽应该是将 props 作为参数的函数 根据 Vue.js 文档

export default {
  render(createElement) {
 
    // ...
    // some other stuff
    // ...

    // Scoped slots in the form of
    // { name: props => VNode | Array<VNode> }
    scopedSlots: {
      default: props => createElement('span', props.text)
    },
  }
}

也许你应该试试

您也可以使用 vue 的新统一 v-slot 系统完成同样的事情

<!-- page component -->
<template>
  <some-component>
    <template v-for="slot in scopedSlots" v-slot:[slot]="props">
      hello {{props}}
    </template>
  </some-component>
</template>

<!-- some-component.vue -->

<template>
  <div>
    <slot v-for="slot in Object.keys($slots)" :name="slot"></slot>
  </div>
</template>

不同之处在于,在您的 reduce 中,您创建的函数为

() => { createElement( 'div', 'hello world') }

在您的硬编码版本中(以及在@Boussadjra 的回答中的 forEach 循环中)它们被创建为

() => createElement('div', 'hello world')

实际上是 return 创建的元素。跟使用reduce没关系,没关系

const ReplicatorComponent = {
  template: `<div>
    <h1>replicator-component</h1>
    <slot name='element_1'></slot>
    <slot name='element_2'></slot>
    <slot name='element_3'></slot>
  </div>`
};

const records = [
  { "index": 1 },
  { "index": 2 },
  { "index": 3 },
];

Vue.component('my-component', {
  render: function(createElement) {
    return createElement(ReplicatorComponent, {
      attrs: {
        elements: records.length
      },
      scopedSlots: records.reduce((a,x) => ({
        ...a, 
        ['element_' + x.index]: () => 
          createElement( 'div', 'hello world')
       }), {})
    });
  },
});

new Vue({
  el: '#app',
  data: () => ({})
});
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>

<div id="app">
  <my-component></my-component>
</div>