如何在 Vue3 中渲染多个插槽?

How to render multiple slots in Vue3?

如何使用 h() 函数在 Vue3 setup() 函数中呈现以下模板?

<label v-for="service in services" :key="service">
  <slot name="before" :service="service"></slot>
    foobar
  <slot name="after" :service="service"></slot>
</label>

h() arguments是:

// @returns {VNode}
h(
  // {String | Object | Function } tag
  // An HTML tag name, a component or an async component.
  // Using function returning null would render a comment.
  //
  // Required.
  'div',

  // {Object} props
  // An object corresponding to the attributes, props and events
  // we would use in a template.
  //
  // Optional.
  {},

  // {String | Array | Object} children
  // Children VNodes, built using `h()`,
  // or using strings to get 'text VNodes' or
  // an object with slots.
  //
  // Optional.
  [
    'Some text comes first.',
    h('h1', 'A headline'),
    h(MyComponent, {
      someProp: 'foobar'
    })
  ]
)

组件的setup() arguments are below. setup() can also return a render function(returns一个VNode来自h()的函数):

setup(
  // {Object} Component prop values
  props,

  // {Object} Contains `attrs`, `emit`, and `slots`
  context
)

上下文的 slots 属性 包含每个给定槽的函数。这些函数接收作为 props 传递给插槽的参数,并且每个函数 returns 对应插槽的 VNode。例如,要获取 before 插槽的 VNode 并传递 service 的插槽属性,请调用 context.slots.before({ service: 'myService' }).

因此,您的模板的等效呈现函数类似于以下内容:

import { h } from 'vue'

export default {
  props: {
    services: {
      type: Array,
      default: () => [],
    },
  },
  setup({ services }, { slots }) {
    return () =>
      services.map((service) =>
        h(                             //
          'label',                     //
          {                            // <label :key="service">
            key: service,              //
          },                           //
          [
            slots.before({ service }), //   <slot name="before" :service="service" />
            'foobar',                  //   foobar
            slots.after({ service })   //   <slot name="after" :service="service" />
          ]
        )                              // </label>
      )
  },
}

demo