vue3 插槽slot

插槽是子组件中的提供给父组件使用的一个占位符,用 <slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot> 元素。<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

一、匿名插槽 

子组件 FancyButton 中插槽模板

# 基础用法
<button class="fancy-btn">
	<!-- 插槽出口 -->
	<slot></slot> 
</button>

# 可指定默认插槽内容:父组件没有提供任何插槽内容时,默认渲染的插槽内容 Submit
<button class="fancy-btn">
	<slot>Submit</slot> 
</button>

父组件填充插槽内容

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

# 或

<FancyButton>
	<!-- 插槽内容 -->
	 <template v-slot>
       <div>Click Me</div>
    </template>
</FancyButton>

最终渲染出的 DOM 结构如下

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

ps. 

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

二、具名插槽 (named slots) 

具名插槽其实就是给插槽取个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中。

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

子组件预留的插槽

<div class="container">
    <header>
    	<slot name="header"></slot>
    </header>
    <main>
    	<-- 没有提供 name 的 slot 出口会隐式地命名为 “default” -->
    	<slot></slot>
    </main>
    <footer>
    	<slot name="footer"></slot>
    </footer>
</div>

 父组件对指定插槽进行填充

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

<BaseLayout>
    <template v-slot:header>
       <div>header</div>
    </template>
    <template v-slot>
       <div>default</div>
    </template>
    <template v-slot:footer>
       <div>footer</div>
    </template>
</BaseLayout>

v-slot 语法糖(简写方式)

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

<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>

三、动态插槽

动态指令参数在 v-slot 上也是有效的,即可以通过变量定义动态插槽名:

<script setup>
	import { ref } from "vue";
	const name = ref('header')
</script>
<template>
	<base-layout>
		<template v-slot:[name]>
            <div>动态插槽</div>
        </template>
	
		 <!-- 缩写为 -->
        <template #[name]>
            <div>动态插槽</div>
        </template>
    </base-layout>
</temppate>

四、作用域插槽

在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。可以使用属性绑定的方式向一个插槽的出口上传递数据,称为插槽 props 。

4.1 匿名插槽数据传递

子组件 MyComponent传递插槽 props

<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>  

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

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

# 或 在 v-slot 中使用解构语法
<MyComponent v-slot="{ text, count }">
	{{ text }} {{ count }}
</MyComponent>

4.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>

4.3 作用域插槽应用场景:高级列表组件示例

你可能想问什么样的场景才适合用到作用域插槽,这里我们来看一个 <FancyList> 组件的例子。它会渲染一个列表,并同时会封装一些加载远端数据的逻辑、使用数据进行列表渲染、或者是像分页或无限滚动这样更进阶的功能。然而我们希望它能够保留足够的灵活性,将对单个列表元素内容和样式的控制权留给使用它的父组件。我们期望的用法可能是这样的:

<FancyList :api-url="url" :per-page="10">
    <template #item="{ body, username, likes }">
        <div class="item">
          <p>{{ body }}</p>
          <p>by {{ username }} | {{ likes }} likes</p>
        </div>
    </template>
</FancyList>

在 <FancyList> 之中,我们可以多次渲染 <slot> 并每次都提供不同的数据 (注意我们这里使用了 v-bind 来传递插槽的 props):

<ul>
  <li v-for="item in items">
    <slot name="item" v-bind="item"></slot>
  </li>
</ul>

上面的 <FancyList> 案例同时封装了可重用的逻辑 (数据获取、分页等) 和视图输出,但也将部分视图输出通过作用域插槽交给了消费者组件来管理

### Vue 3 Slot 插槽传值的方法 在 Vue 3 中,插槽Slot)提供了一种强大的方式来组合组件的内容。为了实现更灵活的设计模式,Vue 提供了具名插槽和作用域插槽两种机制用于传递数据。 #### 使用具名插槽传递静态内容 当需要向特定位置插入内容时,可以使用具名插槽。这允许父组件决定子组件内部某些部分的具体呈现形式而不改变其结构逻辑[^1]。 ```html <!-- ParentComponent.vue --> <ChildComponent> <!-- 默认插槽 --> <template v-slot:default>这是默认插槽</template> <!-- 具名插槽 --> <template v-slot:header>这里是头部区域</template> <template v-slot:footer>这里是底部区域</template> </ChildComponent> ``` 对于上述代码片段,在 `ChildComponent` 组件内定义好了接收这些模板的位置之后,就可以按照预期显示来自父级的不同部分内容[^2]。 #### 利用作用域插槽传递动态数据 如果希望不仅仅是简单地填充 HTML 片段,而是想要共享一些状态或属性给到子组件,则需要用到带有作用域的作用域插槽。这种方式可以让子组件将其自身的私有数据暴露出来作为上下文的一部分传递回父组件,从而让后者能够访问并利用这部分信息构建更加复杂的交互体验。 ```html <!-- ChildComponent.vue --> <template> <div class="container"> <slot :user="user"></slot> </div> </template> <script setup lang="ts"> import { ref } from &#39;vue&#39;; const user = ref({ name: &#39;张三&#39;, age: 28 }); </script> ``` 接着可以在父组件里像这样获取并展示这个对象: ```html <!-- ParentComponent.vue --> <ChildComponent v-slot="{ user }"> {{ user.name }} ({{ user.age }}) </ChildComponent> ``` 这段代码展示了如何通过解构赋值的方式轻松取得由子组件提供的变量,并直接嵌入至最终渲染的结果之中。 #### 完整示例:带参数的具名插槽 下面给出一个完整的例子说明怎样在一个实际项目里面运用所学的知识点完成具体功能开发——比如创建一个可配置按钮组控件,其中每个按钮都可以自定义样式及行为。 ```html <!-- ButtonGroup.vue --> <template> <ul> <li v-for="(item, index) in items" :key="index"> <button @click="$emit(&#39;select&#39;, item)"> <slot :name="&#39;btn-&#39; + index" :text="item.text">{{ item.defaultText }}</slot> </button> </li> </ul> </template> <script setup lang="ts"> defineProps({ items: { type: Array, required: true } }); </script> ``` 然后在调用的地方可以根据需求定制每一个按钮的样子: ```html <ButtonGroup :items="[...]" @select="handleSelect"> <template v-slot:btn-0="{ text }">首页 - {{ text }}</template> <template v-slot:btn-1="{ text }"><strong>{{ text }}</strong></template> ... </ButtonGroup> ``` 这里不仅实现了对单个元素外观上的差异化处理,同时也保持了良好的模块化设计原则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值