插槽
在VUE开发项目的过程中,插槽<slot>
是重要的承载分发内容的出口。基本使用如下:
- 在父组件中引入子组件,并在引入子组件的标签中包裹需要传递给子组件
<slot>
的内容:内容何以时字符串、模板代码,甚至组件; - 在子组件中要包含插槽
<slot></slot>
,否则父组件带传递的内容将不会传递; - 当组件渲染的时候,
<slot></slot>
将会被替换为“插槽内容:字符串、模板代码、组件”:
// 父组件
<section style="margin:50px;padding:10px;width:500px;border:1px solid red;">
<test-slot>
插槽内容:字符串、模板代码、组件
</test-slot>
</section>
// 子组件模板
<template>
<div style="border:1px solid green;">
<slot></slot>
</div>
</template>
效果:
注意:
- 此处插槽传递的内容可以是父级模板里作用域的所有内容,即父级模板的内容都是在父级作用域编译,同样,子模板里内容也都是在子作用域中编译的,两者互不掺联。
备用内容
在插槽使用过程中,可以在子组件中插槽设置备用、默认的内容 <slot>我是备用内容</slot>
,只在没有提供插槽内容的时候渲染。
// 父组件模板
<test-slot></test-slot>
<test-slot>替换插槽默认内容</test-slot>
<test-slot></test-slot>
// 子组件模板
<div style="border:1px solid green;margin:5px;">
<slot>我是备用内容</slot>
</div>
效果:
具名插槽
在需要多个插槽并分别给不同插槽传递不同内容时,解决办法:
- 给子模板
<slot>
设置name
属性; - 给父组件使用
template
包裹待传递内容,并添加v-slot
属性,属性值与子模版<slot>
的name
属性对应值。
// 父组件模板
<test-slot>
<template v-slot:header>
<h1>I'm header Title</h1>
</template>
<template v-slot:default>
<p>I'm a paragraph for the main content.</p>
</template>
<template v-slot:footer>
<h2>I'm footer</h2>
</template>
</test-slot>
// 子组件模板
<template>
<div style="border:1px solid green;margin:5px;">
<header>
<slot name="header"></slot>
</header>
<main>
// 一个不带 `name` 的 `<slot>` 出口会带有隐含的名字“default”。
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
效果:
注意,v-slot
只能添加在 <template>
上
具名插槽的缩写
v-slot:
可以简写为#
,v-slot:header
→#header
<test-slot>
<template #header>
<h1>I'm header Title</h1>
</template>
<template#default>
<p>I'm a paragraph for the main content.</p>
</template>
<template #footer>
<h2>I'm footer</h2>
</template>
</test-slot>
注意:在使用缩写时,必须确定#
后有参数,否则会致使语法失效触发警告。
<test-slot>
<template #="{item, index}">
🍎 <span style="color:orange;">{{index}}---{{item}}</span>
</template>
</test-slot>
如果没有参数仍然希望使用缩写,必须始终以明确插槽名取而代之:
<test-slot>
<template #default="{item, index}">
🍎 <span style="color:orange;">{{index}}---{{item}}</span>
</template>
</test-slot>
作用域插槽:
在插槽内容访问子组件中才有的数据,并对在父模板中对插槽内容自定义时,作用域插槽就有了永无之地。
// 父组件模板
<section style="margin:50px;padding:10px;width:500px;border:1px solid red;">
<test-slot>
// 在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:
// 这里把包含所有插槽 prop 的对象命名为:slotProps
<template v-slot:default="slotProps">
🍎 <span style="color:orange;">{{ slotProps.index }}-{{ slotProps.item }}</span>
</template>
</test-slot>
</section>
// 子组件模板
<template>
<div style="border:1px solid green;margin:5px;">
<ul>
<li v-for="(item, index) in items" :key="index">
// 根据自己的需要将很多的 attribute 绑定到 slot 上
// 绑定在 <slot> 元素上的 attribute 被称为插槽 prop。
<slot :item="item" :index="index"></slot>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'test',
data() {
return {
items: ['Feed a cat', 'Buy milk']
}
},
}
效果:
独占默认插槽的缩写语法
在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用,可以把v-slot
直接用写在组件上,其他情况下v-slot
必须写在template
标签内:
// 父组件模板
<section style="margin:50px;padding:10px;width:500px;border:1px solid red;">
<test-slot v-slot:default="slotProps">
// 在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:
// 这里把包含所有插槽 prop 的对象命名为:slotProps
🍎 <span style="color:orange;">{{ slotProps.index }}-{{ slotProps.item }}</span>
</test-slot>
</section>
或者
// 父组件模板
<section style="margin:50px;padding:10px;width:500px;border:1px solid red;">
// 不带参数的 v-slot 被假定对应默认插槽:
<test-slot v-slot="slotProps">
// 在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:
// 这里把包含所有插槽 prop 的对象命名为:slotProps
🍎 <span style="color:orange;">{{ slotProps.index }}-{{ slotProps.item }}</span>
</test-slot>
</section>
注意:
- 默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
// 父组件模板
<section style="margin:50px;padding:10px;width:500px;border:1px solid red;">
<test-slot v-slot="slotProps">
🍎 <span style="color:orange;">{{ slotProps.index }}-{{ slotProps.item }}</span>
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</test-slot>
</section>
有多个插槽时,始终使用完整的基于<template>
的语法:
// 父组件模板
<section style="margin:50px;padding:10px;width:500px;border:1px solid red;">
<test-slot>
<template v-slot:default="slotProps">
🍎 <span style="color:orange;">{{ slotProps.index }}-{{ slotProps.item }}</span>
</template>
<template v-slot:other="otherSlotProps">
🍎 <span style="color:blue;">{{ otherSlotProps.index }}-{{ otherSlotProps.item }}</span>
</template>
</test-slot>
</section>
// 子组件模板
<template>
<div style="border:1px solid green;margin:5px;">
<ul>
<li v-for="(item, index) in items" :key="index">
<slot :item="item" :index="index"></slot>
</li>
</ul>
<ul>
<li v-for="(item, index) in items2" :key="index">
<slot name="other" :item="item" :index="index"></slot>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'test',
data() {
return {
items: ['Feed a cat', 'Buy milk'],
items2: ['CCat', 'MMilk']
}
},
}
效果:
解构插槽 Prop
作用域插槽的v-slot
的值可以是任何能够作为函数定义中的参数的JavaScript
表达式。
// 父组件模板
<section style="margin:50px;padding:10px;width:500px;border:1px solid red;">
<test-slot>
// 解构
<template v-slot="{item, index}">
🍎 <span style="color:orange;">{{index}}---{{item}}</span>
</template>
</test-slot>
<test-slot>
// 解构+重命名
<template v-slot="{item:todo, index:idx}">
🍌 <span style="color:orange;">{{idx}}---{{todo}}</span>
</template>
</test-slot>
<test-slot>
// // 解构+重命名+自定义备用内容
<template v-slot="{item:todo, index:idx, other = 'Placeholder'}">
🍊 <span style="color:orange;">{{idx}}---{{todo}}---{{other}}</span>
</template>
</test-slot>
</section>
效果:
动态插槽
插槽名称使用动态变量,可以自行设定事件改变变量,实现对插槽内容传递的控制。
// 父组件模板
<test-slot>
<template v-slot:[dynamicSlotName]>
<h1>I'm header Title</h1>
</template>
</test-slot>
<script>
export default{
data(){
return {
dynamicslotname:'header'
}
},
}
</script>