之前的文章里介绍了vue3中父传子props方法可以传递js中任意类型的数据。那么如何向子组件中传入模版呢,很多时候组件里面需要渲染一些自定义的模块,根据不同的场景显示不同的内容,此时传递模版就更方便,这就要用到slot也就是插槽了。文中的例子也都是使用vue3的语法。
插槽(slot)是一种非常强大和灵活的机制,用于在组件中传递内容。在组件中通过插槽定义模板,然后在使用的时候填充具体的内容。
在组件中,你可以使用 元素来定义插槽的出口,表示一个可插入内容的位置。在父组件中,可以通过插入内容到这个插槽来填充它。
组件模板:
<template>
<div>
<h1>组件</h1>
<slot></slot>
</div>
</template>
使用
<script setup>
import SComponent from './SComponent.vue';
</script>
<template>
<SComponent>
<p>父组件的内容将插入到子组件的插槽中。</p>
</SComponent>
</template>
</script>
插槽中的内容可以访问父组件中的数据和方法。这意味着插槽内容在父组件的作用域内进行渲染,可以使用父组件的数据和方法。
就比如下面的例子里面,这里两个插值表达式渲染的内容是一样的。
<script setup>
import { ref } from 'vue';
import SComponent from './SComponent.vue';
const title = ref("标题");
</script>
<template>
<div>{{ title }}</div>
<SComponent>
<p>{{ title }}</p>
</SComponent>
</template>
</script>
但是插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。
很多时候不需要传入内容,默认就显示的模块,叫做默认插槽。注意:一个组件里面只能定义一个默认插槽
比如一个公共组件SubmitButton.vue
<template>
<botton>
<slot>
确定
</slot>
</button>
</template>
使用:在没有传入内容时,默认就展示 确定
。如果父组件传递了内容就使用传递的。
<SubmitButton />
此时就会显示保存
<SubmitButton>保存</SubmitButton>
有时候一个组件里面需要多个插槽,为了确定渲染的内容不出错,就需要给占位的槽位取个名字,使用时根据名字就能分别渲染出来具体的内容了。
比如:SlotComponent.vue
<template>
<div>
<h1>组件</h1>
// 具名插槽
<slot name="header"></slot>
// 默认插槽
<slot></slot>
</div>
</template>
使用:需要通过v-slot指令给插槽传递内容,v-slot 有对应的简写 #,下面的<template v-slot:header>
也就等同于<template #header>
<template>
<slot-component>
<template v-slot:header>
<p>这是头部插槽</p>
</template>
<p>这是默认内容</p>
</slot-component>
</template>
<script setup>
import SlotComponent from './SlotComponent.vue';
</script>
当然可以有多个不同名字的插槽,特点就是使用了name
属性,没有使用name
属性的就是默认插槽。用法如上。
作用域插槽允许子组件向插槽传递数据,使插槽内容可以使用子组件中的数据。虽然上面说到了插槽的内容无法访问到子组件的状态。但是某些场景还是需要使用子组件内部的数据。可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes:
<template>
<div>
<h1>组件</h1>
<slot :data="someData"></slot>
</div>
</template>
<script setup>
import { ref } from 'vue';
const someData = ("一些内容");
</script>
使用:
<template>
<slot-component>
<template v-slot="slotProps">
<p>{{ slotProps.data }}</p>
</template>
</slot-component>
</template>
<script>
import SlotComponent from './SlotComponent.vue';
</script>
子组件传递的参数也支持结构语法,上面的代码等同于:
<template>
<slot-component>
<template v-slot="{ data }">
<p>{{ data }}</p>
</template>
</slot-component>
</template>
<template>
<div>
<h1>组件</h1>
<slot name="xxx" :data="someData"></slot>
</div>
</template>
<script setup>
import { ref } from 'vue';
const someData = ("一些内容");
</script>
使用:可以使用v-slot:xxx="props"
也可以简写#xxx="props"
<template>
<slot-component>
<template #xxx="{ data }">
<p>{{ data }}</p>
</template>
</slot-component>
</template>
在同时使用具名插槽和默认插槽时应该用<template>
包裹,这样更明确props作用域。
<template>
<div>
<h1>组件</h1>
<slot :message="someData"></slot>
<slot name="footer"></slot>
</div>
</template>
<script setup>
import { ref } from 'vue';
const someData = ("一些内容");
</script>
使用:
<template>
<MyComponent>
<!-- 使用显式的默认插槽 -->
<template #default="{ message }">
<p>{{ message }}</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</MyComponent>
</template>
注意事项:
插槽名称:作用域插槽使用 v-slot
指令,并通过 v-slot
指定一个变量名,用于接收子组件传递的数据。确保你理解如何正确使用插槽名称。
<!-- 在这个例子中,插槽名称为 "data" -->
<template v-slot:data="{ prop1, prop2 }">
<!-- 使用 prop1 和 prop2 -->
</template>
作用域冲突: 当插槽内使用的变量名与当前作用域中的变量名相同时,可能会引发冲突。避免使用相同的变量名,或者使用修饰符如 v-slot:data="{ data: slotData }"
来避免冲突。
清晰的传递数据:在使用作用域插槽时,确保数据的传递清晰明了,这样维护和理解代码将更加容易。
适度使用: 不是所有的场景都需要使用作用域插槽,因此请根据具体情况决定是否使用。对于简单的插槽需求,普通插槽已经足够。
维护性: 尽管作用域插槽非常强大,但在一些情况下可能使模板变得更加复杂。在考虑可维护性时,确保代码结构清晰,易于理解。