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

Posted

技术标签:

【中文标题】在 Render 函数中构建动态数量的 Vue 插槽【英文标题】:Build a dynamic number of Vue slots within a Render function 【发布时间】:2021-03-14 15:00:12 【问题描述】:

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

这个正在渲染的组件接受任意数量的插槽。在下面的示例中,有三个可用的插槽(名为 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') ), )
  );
,

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

【问题讨论】:

【参考方案1】:

不同之处在于,在您的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>

【讨论】:

【参考方案2】:

我认为作用域插槽应该是以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>

【讨论】:

【参考方案3】:

reduce 方法不起作用,因为它在createElement('div', 'hello world') 之前缺少返回:

完整示例

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>

【讨论】:

以上是关于在 Render 函数中构建动态数量的 Vue 插槽的主要内容,如果未能解决你的问题,请参考以下文章

render函数

Vue中render渲染函数详解

Render函数详解

在vue中使用render函数

Vue render函数认识和使用

Vue 基础 - 自定义指令和 render 函数