如何使用 createElement 创建槽的副本?

How to create a copy of the slot with createElement?

如何获取插槽组件的名称?

我想创建插槽中提供的组件的副本:

const child1 = slot
const child2 = h(???, slot.props)

以便 child1 完全呈现 child2

我需要这个,以便我可以更改该 VNode 的属性,例如 类。

上下文

import { h } from 'vue';

export default {
  setup(props, { slots }) {
    const children = [];

    for (const slot of slots.default()) {
      const child = h(???, slot.props)
      children.push(h('div', [child]));
    }

    return () =>
      h('div', children);
  },
};

背景

我想做一个类似于q-button-group的组件:

我需要 2 个组件 TButtonTButtonGroup 以便我可以独立地设置 TButton 样式并通过将这些按钮放在 TButtonGroup.[=25 中来创建组=]

例子

  <TButtonGroup>
    <TButton label="Two" />
    <TButton label="Three" />
  </TButtonGroup>

TButton 应该有一个不同的列表 类:

See full html

游乐场

https://stackblitz.com/edit/vue3-button-group-razbakov?file=src%2Fcomponents%2FGroupRender.js

vnode的组件名就不多说了,此时组件已经解析了。 VNode的元素或组件存储在type 属性.

您的方法的问题在于渲染函数是组件模板的替代方法,而不是访问整个 DOM 元素层次结构的方法。渲染函数中不会有 TButton 子元素,如 div,只有 TButton vnode 本身。需要渲染它才能访问其子项。

如果 TButton 是需要修改初始行为的其他组件,这可以通过向其添加一些指令并访问组件的子元素来完成。

但是由于 TButton 是您自己的组件,可以根据您的需要进行修改,所以最直接的方法是根据道具更改 类 并在其内部提供此道具 TGroup,即:

const child = h(slot.type, {...slot.props, group: true}, slot.children);
children.push(child);

使用您创建的组件类型:

const { h } = Vue

const TBtn = {
  props: {
    staticClass: {
      type: Array,
      default: () => [],
    },
  },
  template: `
    <div
      class="t-btn px-4 py-2"
    >
      <slot></slot>
    </div>
  `
}

const TBtnGroup = {
  setup(props, {
    slots
  }) {
    const children = [...slots.default()]
      .map(slot => h(slot.type, {
        class: ['border', 'rounded-lg']
      }, slot))

    return () => h('div', {
      class: ['d-flex']
    }, children)
  },
}

const App = {
  template: `
    <t-btn>OUTSIDE 1</t-btn>
    <t-btn>OUTSIDE 2</t-btn>
    <br />
    <t-btn-group>
      <t-btn>INSIDE 1</t-btn>
      <t-btn>INSIDE 2</t-btn>
      <t-btn>INSIDE 3</t-btn>
    </t-btn-group>
  `
}

const app = Vue.createApp(App)
app.component('TBtn', TBtn)
app.component('TBtnGroup', TBtnGroup)

app.mount('#app')
.px-4 {
  padding-left: 16px;
  padding-right: 16px;
}

.py-2 {
  padding-top: 8px;
  padding-bottom: 8px;
}

.border {
  border: 1px solid black;
}

.rounded-lg {
  border-radius: 8px;
}

.d-flex {
  display: flex;
  gap: 8px;
}

.t-btn:hover {
  cursor: pointer;
  background-color: black;
  color: white;
}
<script src="https://unpkg.com/vue@next"></script>

<div id="app"></div>