Vue 小技巧 - 将插槽传递给子组件

3,432 阅读5分钟

介绍

作为最顶级的 JavaScript 框架之一,Vue 值得关注。它非常容易学习、使用,并且让您在接触它后立即爱上它的做事方式。

在本文中,我们将讨论它的一个强大功能,即插槽,并简要说明它们是什么以及如何使用它们。最后,重点是将所有插槽从父组件传递给子组件。

本文假设您对 Vue 插槽及其工作原理有所了解

不过,我将提供一些简短的解释。好的,让我们深入了解一下。

什么是 Vue 插槽?

我们可以将它们定义为自定义元素,使我们能够为我们的组件创建自定义的动态内容。它们具有保留名称slot,我们可以将它们创建为默认或命名插槽。此外,它们可以被解释为预定义的组件。

每个插槽都有自己的范围和缺省内容。插槽的范围与我们使用插槽的模板的范围相同,这意味着我们的插槽将无法访问它代表内容的组件的范围。在下一节中,您可以看到示例,希望前面的解释会更清楚。

接下来缺省内容,即我们插槽的开始和结束标记之间的内容,它代表我们希望在使用我们的组件的模板中未提供我们的插槽的情况下显示的默认值。基本上,我们确保页面上始终显示某些内容,无论是插槽内容还是默认值。

如何使用 Vue 插槽?

为了从外部为我们的组件传递动态内容,我们需要做的就是slot将组件模板中的元素放置在我们希望自定义内容出现的位置。查看一个快速示例组件:

组件模板

<template>
  <div>
    <span>Inner Content</span>
    <slot>Default Content</slot> <!-- This is where the maging happens -->
  </div>
</template>

并且,在我们的应用程序中的某个地方进行使用

<example>
  This text is going to be rendered instead of the element within our example component
</example>

如果我们像这样使用组件:

<example />

缺省值将被呈现,在我们的例子中是Default Content文本。

没有名称的插槽将通过 Vue 框架将此属性设置为“默认”

如何使用Vue命名插槽?

Vue 命名槽只不过是一个name配置了属性的槽元素。当我们希望在组件模板中的不同位置呈现不同的动态内容时,这非常有用。让我们更新示例组件。

<template>
  <section>
    <header>
      <slot name="title">Title</slot>
    </header>
    <main>
      <slot>Content</slot>
    </main>
  </section>
</template>

在我们的应用程序中的某个地方进行调用

<example>
  <template v-slot:title>
    My Component Title
  </template>
  <p>My Component Content</p>
</example>

正如您可以假设的那样,模板元素中的所有内容都将在我们组件内相应插槽的位置呈现,而其外部的所有内容都将在默认插槽的位置呈现。我们通过使用v-slot指令来实现这一点,后跟我们想要传递动态内容的插槽的名称。

如何使用Vue Scoped Slots?

在上一节中,我已经提到插槽的范围与我们使用它的模板的范围相同。有时,这还不够好,在某些情况下,我们希望拥有子组件的作用域。幸运的是,Vue 支持这一点,并让我们能够在组件之间传递作用域。让我们用一个例子来支持这一点。

我们的组件模板:

<template>
  <section>
    <slot>{{user.name}}</slot>
  </section>
</template>

和js:

export default {
  name: 'example',
  data() {
    return {
      user: {
        name: 'John',
        surname: 'Doe'
      }
    }
  }
}

在我们应用程序的某个地方,我们像这样使用组件:

<example>
  Fallback content
</example>

此时,我们的回退内容不能基于user数据,因为数据是在示例组件中定义的,而插槽的内容是在父组件中呈现的。

这就是作用域插槽出现的地方。要将作用域传递给父级,我们需要将示例组件模板更改为如下所示:

<template>
  <section>
    <slot v-bind:user="user">{{user.name}}</slot>
  </section>
</template>

通过v-bind指令,我们将用户对象绑定到用户属性,现在,我们可以使用父组件模板中的用户数据。

<example>
  <template v-slot:default="props">
    {{ props.user.name }} {{props.user.surname }}
  </template>
</example>

在上一节中,我已经提到未命名插槽隐式地具有名称“默认”,并且通过v-slot指令,我们可以告诉框架哪个插槽应该具有提供的回退内容。

将插槽传递给子组件

好的,这就是我们来这里的原因。现在是展示我们如何将所有插槽从父组件传递给子组件的时候了。

正如您所猜测的,我们需要创建一个带有插槽的模板,因为它是我们想要从父级传递给子级的每个插槽的内容,并且有两种方法可以做到这一点。第一种方法是手动完成,为每个插槽创建一个模板元素,如果我们有一个或两个插槽就可以了,但是如果我们有更多的插槽要传递怎么办?我们是否假设n在我们的父组件中有模板?嗯,不,这是一个非常冗长和不切实际的解决方案,在这种情况下,我们将使用第二种方法,并使用for...each循环动态渲染模板。

您可能想知道我们要迭代哪些集合?别担心,Vue 已经涵盖了这些!我们定义的每个插槽都将存储在以下集合之一中:

  • $slots
  • $scopedSlots

从 2.6.0+ 开始,所有 slots现在也作为函数暴露在slots 现在也作为函数暴露在 scopedSlots 上

上面的文字可能会让人不知所措,但我希望下面的例子能帮助你理解。

传递代码$slots

<template v-for="(index, name) in $slots" v-slot:[name]>
  <slot :name="name" />
</template>

传递 的代码$scopedSlots需要更多配置,但它类似于传递$slots

<template v-for="(index, name) in $scopedSlots" v-slot:[name]="data">
  <slot :name="name" v-bind="data"></slot>
</template>

通过在我们的父组件中定义这样的模板,我们传递了在我们使用父组件的地方定义的所有插槽。这是一个完整的例子:

点击在codepen中查看

如您所见,我们child-slot在子组件中定义了 a并在应用程序根中为其提供了内容。父组件只是将所有插槽传递给子组件,这就是我写这篇文章想向您展示的。

您可以在我创建的开源 Vue 组件上看到这种方法的实际效果。它被称为 Vue GridMultiselect。在GitHub 上查看。