Vue - 如何在包装组件中传递插槽?

90

所以我创建了一个简单的包装组件,模板如下:

<wrapper>
   <b-table v-bind="$attrs" v-on="$listeners"></b-table>
</wrapper>

使用$attrs$listeners传递属性和事件。这很好用,但是包装器如何将<b-table>命名插槽代理到子项中呢?


请注意,这个问题将在这个新的PR中得到解决。但是,如果你想立即得到解决方案,可以参考这个Github评论。已测试:https://jsfiddle.net/jacobgoh101/bptLavov/185/ - Jacob Goh
4个回答

186

Vue 3

与下面的Vue 2.6示例相同,除了以下几点:

  • $listeners已合并到$attrs中,因此不再需要v-on="$listeners"。请参阅迁移指南
  • $scopedSlots现在只是$slots。请参阅迁移指南
<template v-for="(_, slot) of $slots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>

Vue 2.6(v-slot 语法)
所有普通插槽都将被添加到作用域插槽中,因此您只需要这样做:
<wrapper>
  <b-table v-bind="$attrs" v-on="$listeners">
    <template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>
  </b-table>
</wrapper>

Vue 2.5

请参阅Paul的答案


原始答案

你需要像这样指定插槽:

<wrapper>
  <b-table v-bind="$attrs" v-on="$listeners">
    <!-- Pass on the default slot -->
    <slot/>

    <!-- Pass on any named slots -->
    <slot name="foo" slot="foo"/>
    <slot name="bar" slot="bar"/>

    <!-- Pass on any scoped slots -->
    <template slot="baz" slot-scope="scope"><slot name="baz" v-bind="scope"/></template>
  </b-table>
</wrapper>

渲染函数
render(h) {
  const children = Object.keys(this.$slots).map(slot => h('template', { slot }, this.$slots[slot]))
  return h('wrapper', [
    h('b-table', {
      attrs: this.$attrs,
      on: this.$listeners,
      scopedSlots: this.$scopedSlots,
    }, children)
  ])
}

你可能还想在组件上将 inheritAttrs 设置为 false。

2
当我使用命名插槽(在Vue 2.6.11上)时,“Vue 2.6”的答案对我不起作用。如果我删除="scope"v-bind="scope"部分,它可以用于命名插槽,但我不能再用于作用域插槽了:( - pouria
我在2.6中遇到了相同的问题。看起来问题在于尝试访问非作用域插槽的范围。Sergey下面的答案对我有用。 - Excalibaard

91

我一直在使用 v-for 自动传递所有插槽,如下所示。通过这种方法的好处是您无需知道哪些插槽必须被传递,包括默认插槽。任何传递到包装器的插槽都将被传递。

<wrapper>
  <b-table v-bind="$attrs" v-on="$listeners">

    <!-- Pass on all named slots -->
    <slot v-for="slot in Object.keys($slots)" :name="slot" :slot="slot"/>

    <!-- Pass on all scoped slots -->
    <template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope"><slot :name="slot" v-bind="scope"/></template>

  </b-table>
</wrapper>

这行代码非常重要,如果你想从父级组件中使用 scoped slots 来为 Vuetify 数据表格指定特定列的 UI,那么它就派上用场了。<template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope"><slot :name="slot" v-bind="scope"/></template> - JeppePepp

14

这里是更新后的vue语法>2.6,包含具有作用域的插槽和常规插槽,感谢Nikita-Polyakov,讨论链接

<!-- pass through scoped slots -->
<template v-for="(_, scopedSlotName) in $scopedSlots" v-slot:[scopedSlotName]="slotData">
  <slot :name="scopedSlotName" v-bind="slotData" />
</template>

<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
  <slot :name="slotName" />
</template>

<!-- after iterating over slots and scopedSlots, you can customize them like this -->
<template v-slot:overrideExample>
    <slot name="overrideExample" />
    <span>This text content goes to overrideExample slot</span>
</template>

8
这适用于Vue 3.2版本及以上的解决方案。
<template v-for="(_, slot) in $slots" v-slot:[slot]="scope">
    <slot :name="slot" v-bind="scope || {}" />
</template>

这段代码很好用,但当插槽作为片段或文本根节点动态创建和渲染时就不行了... 然后在开发控制台中会得到大量的 Vue Warn 警告。当我想要包装 Kendo UI Grid 组件并公开其单元格模板插槽时,我遇到了这个问题。 - incutonez
1
这是什么让它特定于3.2版本? - Estus Flask
我在[slot]处遇到了一个TypeScript错误:Element implicitly has an 'any' type because expression of type 'string | number' can't be used to index type {...}。你有什么解决方法吗? - Gannet
一样的情况。就我所知,它似乎运行正常,但是VSCode将其标记为类型错误。如果我使用这种方法,我的所有包装组件都会在资源管理器中被标记为带有错误的文件,可能会掩盖后续的真实错误... - Partap Davis
我找到了修复TypeScript错误的方法(无意中):您需要import您正在包装的组件,即使它已经全局注册。(例如,如果您要包装Quasar的QInput组件,您需要在您的组件中添加import { QInput } from 'quasar'。) - Partap Davis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接