Vue2组件中的插槽

目录

一、插槽内容与出口

二、插槽渲染作用域

三、插槽默认内容

四、具名插槽

五、作用域插槽

六、动态插槽名


一、插槽内容与出口

组件如何接收模板内容?在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些模版片段。vue通过使用slot插槽实现。

<FancyButton> 组件:

<button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 -->
</button>

 父组件中使用自组件FancyButton:

<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>

 <slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

最终渲染出的 DOM :

<button class="fancy-btn">Click me!</button>

 通过使用插槽,<FancyButton> 组件更加灵活和具有可复用性。现在组件可以用在不同的地方渲染各异的内容,但同时还保证都具有相同的样式。

二、插槽渲染作用域

插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。

<span>{{ message }}</span>
<FancyButton>{{ message }}</FancyButton>

以上代码中,两个 {{ message }} 插值表达式渲染的内容都是一样的,都是父组件作用域的变量message。

插槽内容无法访问组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。

父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。

三、插槽默认内容

在组件标签的标签体中,没有提供任何内容的情况下,可以为插槽指定默认内容。

以下自组件模版中slot标签的标签体中,写入默认显示的内容:

<button type="submit">
   <slot>
      Submit <!-- 默认内容 -->
   </slot>
</button>

 以下使用子组件的时候没有提供任何slot内容:

<SubmitButton />

 当没有提供任何slot内容时,将会渲染默认内容:

<button>Submit</button>

 但如果我们提供了插槽内容:

<SubmitButton>Save</SubmitButton>

 那么显式提供的内容会取代默认内容:

<button type="submit">Save</button>

四、具名插槽

有时在一个组件中包含多个插槽出口是很有用的。举例来说,在一个 <BaseLayout> 组件中,有如下子组件模板:

<div class="container">
  <header>
    <!-- 标题内容放这里 -->
  </header>
  <main>
    <!-- 主要内容放这里 -->
  </main>
  <footer>
    <!-- 底部内容放这里 -->
  </footer>
</div>

对于这种场景,<slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容: 

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

 这类带 name 的插槽被称为具名插槽 (named slots)。没有提供 name 的 <slot> 出口会隐式地命名为“default”。

在父组件中使用 <BaseLayout> 时,我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了。

要为具名插槽传入内容,我们需要使用一个含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令:

<BaseLayout>
  <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
  </template>
</BaseLayout>

v-slot 有对应的简写 #,因此 <template v-slot:header> 可以简写为 <template #header>。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。 

下面我们给出完整的、向 <BaseLayout> 传递插槽内容的代码,指令均使用的是缩写形式:

<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

当一个组件同时接收默认插槽和具名插槽时,所有位于顶级的非 <template> 节点都被隐式地视为默认插槽的内容。所以上面也可以写成: 

<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <!-- 隐式的默认插槽 -->
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

 现在 <template> 元素中的所有内容都将被传递到相应的插槽。最终渲染出的 HTML 如下:

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

五、作用域插槽

渲染作用域提到,插槽的内容无法访问到子组件的状态。

但在某些场景下插槽的内容可能想要,同时使用父组件域内和子组件域内的数据。vue提供了一种方法来让子组件在渲染时将一部分数据提供给插槽。

1、默认插槽的作用域插槽使用:

传递 props :向一个插槽的出口上传递 属性,

<!-- <MyComponent> 的模板 -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

 接收插槽 props :通过子组件标签上的 v-slot 指令,直接接收一个插槽 props 对象。

子组件传入插槽的 props 作为了 v-slot 指令的值,可以在插槽内的表达式中访问。

<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>

对于v-slot="slotProps" 我们也可以在 v-slot 中使用解构: 

<MyComponent v-slot="{ text, count }">
  {{ text }} {{ count }}
</MyComponent>

2、具名插槽的作用域插槽的使用:

默认插槽和具名插槽,作用域插槽的使用方式有一些小区别。

具名作用域插槽的工作方式也是类似的,插槽 props 可以作为 v-slot 指令的值被访问到:v-slot:name="slotProps"。当使用缩写时是这样:

<MyComponent>
  <template #header="headerProps">
    {{ headerProps }}
  </template>

  <template #default="defaultProps">
    {{ defaultProps }}
  </template>

  <template #footer="footerProps">
    {{ footerProps }}
  </template>
</MyComponent>

向具名插槽中传入 props:

插槽上的 name 是一个 Vue 特别保留的 attribute,不会作为 props 传递给插槽。因此最终 headerProps 的结果是 { message: 'hello' }

<slot name="header" message="hello"></slot>

如果你同时使用了具名插槽与默认插槽,则需要为默认插槽使用显式的 <template> 标签。尝试直接为组件添加 v-slot 指令将导致编译错误。这是为了避免因默认插槽的 props 的作用域而困惑。举例: 

<!-- 该模板无法编译 -->
<template>
  <MyComponent v-slot="{ message }">
    <p>{{ message }}</p>
    <template #footer>
      <!-- message 属于默认插槽,此处不可用,在组件标签上接受的输入默认插槽的props -->
      <p>{{ message }}</p>
    </template>
  </MyComponent>
</template>

 为默认插槽使用显式的 <template> 标签有助于更清晰地指出 message 属性在其他插槽中不可用:

<template>
  <MyComponent>
    <!-- 使用显式的默认插槽,在具名插槽上接受的props,属于该剧名插槽的,在其他具名插槽中不能使用 -->
    <template #default="{ message }">
      <p>{{ message }}</p>
    </template>

    <template #footer>
      <p>Here's some contact info</p>
    </template>
  </MyComponent>
</template>

六、动态插槽名

动态指令参数在 v-slot 上也是有效的,即可以定义下面这样的动态插槽名。

动态插槽名相对比较少用。

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值