Vue插槽-学习笔记
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。
它允许你像这样合成组件:
<todo-button> <!--使用todo-button组件-->
Add todo
</todo-button>
插槽简单来说就是 挖坑 — 填坑 (slot就是这个坑,但不是说slot的中文意思是坑,slot翻译过来为 狭槽,水沟 等意思)
既然Vue实现了这一API,那么故事开始了。。。
默认插槽
<!--使用API--> <!--我想使用todo-button组件,但是我想给它在加点东西-->
<todo-button>
Add todo
</todo-button>
<!-- todo-button的template内容--> <!--组件说:你想给我加东西?加到哪?-->
<button class="btn-primary">
<slot></slot> <!--我说:你把这挖个抗,我把东西放进来。于是组件挖了个坑(slot)-->
</button>
<!--当组件渲染的时候,<slot></slot> 将会被替换为“Add Todo”。-->
(组件说:你以为我就挖了个坑是青铜,其实我是王者。。我挖的坑,你想放什么都行)
插槽中不仅可以放字符串!插槽还可以包含任何模板代码,包括 HTML:或其他组件
备用内容
(组件说:你以为这样就完了?其实我在第五层。。我不仅挖了坑,我的坑没放东西,我还告诉你,你没放)
<slot>你没放东西!!!你没放东西!!!你没放东西<slot> <!--slot里面的数据就是备用内容了-->
当我们在一个父级组件中使用子组件并且不提供任何插槽内容时,备用内容将会被渲染,但是如果我们提供内容,则这个提供的内容将会被渲染从而取代备用内容。
具名插槽
但是我说:我有很多东西要加,一个坑少了,你起码得来亿个坑
组件说:那好,(于是组件真挖了亿个坑) --------问题来了:这么多东西放什么坑?我具体放哪个坑呢?
于是我们协商,把 东西 和 坑 都起个名字,好一一对应
<!--我来用组件-->
<todo-button>
<template v-slot:header> <!--于是我就把这个东西用template包起来,而且用v-slot告诉组件放什么坑-->
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</todo-button>
<!--组件模块--> <!--组件说:我也挖了这么几个坑,我用slot的attribute中的name命名了,等你来放-->
<button class="btn-primary">
<slot name="header"></slot>
<slot></slot> <!--组件说:我们合作这么久了,我不写名字,你应该知道就是默认(default)吧-->
<slot name="footer"></slot>
</button>
**一个不带 name
的 <slot>
出口会带有隐含的名字“default”。**也就是说: name=“defalut”
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
可以根据自己的需要将很多的 attribute 绑定到 slot
上。
绑定在 <slot>
元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop 的名字:
注意,v-slot
只能添加在 <template>
上 (只有一种例外情况-----那就是独占默认插槽的缩写语法)
作用域组件
好家伙!我不仅想放东西,而且我看到组件里面的数据很香,我也想用。怎么办
组件说:不行! 我们不在同一个作用域
作用域:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
我又开始想办法。。。于是乎,我又想到了
我说:组件啊,我放东西的时候,你把数据给我用
组件说:行吧,那我把数据都放到这个坑(slot)上面的属性,完了你自个拿去用
<todo-button>
<template v-slot:default="slotProps"> <!--我说:好,那我就随便取个名字(slotProps)吧-->
<span class="green">{{ slotProps.item }}</span> <!--我用的时候就这么用-->
</template>
</todo-button>
<button class="btn-primary">
<slot name="header"></slot>
<slot :item="item"></slot> <!--组件说:那我把数据都放到坑(slot)上面的属性,完了你自个拿去用-->
<slot name="footer"></slot>
</button>
:item = "item"
表示 使用 v-bind 绑定 属性名为item 值为 item 这个变量的数据
解构插槽 Prop
作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里:(你品,你细品)
function (slotProps) {
// ... 插槽内容 ...
}
这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:
<todo-list v-slot="{ item }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 item
重命名为 todo
:
<todo-list v-slot="{ item: todo }">
<i class="fas fa-check"></i>
<span class="green">{{ todo }}</span>
</todo-list>
你甚至可以定义备用内容,用于插槽 prop 是 undefined 的情形:
<todo-list v-slot="{ item = 'Placeholder' }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
独占默认插槽的缩写语法
在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上:
<todo-list v-slot:default="slotProps"> <!--你会发现这里的v-slot没有用在template标签上-->
<i class="fas fa-check"></i> <!--而是直接使用在todo-list组件标签上-->
<span class="green">{{ slotProps.item }}</span>
</todo-list>
这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot
被假定对应默认插槽:
<todo-list v-slot="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</todo-list>
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<!-- 无效,会导致警告 -->
<todo-list v-slot="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</todo-list>
只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template>
的语法:(官方都这么说,那么全用template,完事)
<todo-list>
<template v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</todo-list>
动态插槽名
动态指令参数也可以用在 v-slot
上,来定义动态的插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
<base-layout>
<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>
</base-layout>
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
<!-- This will trigger a warning -->
<todo-list #="{ item }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
<todo-list #default="{ item }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
总结
- 插槽
<slot>
不仅可以放字符串!插槽还可以包含任何模板代码,包括 HTML:或其他组件 - 多个插槽时,使用
name
属性来命名插槽,同时使用v-slot:插槽名
来确定内容放入什么插槽 - 如果想使用有插槽的组件中的数据,根据自己的需要将很多的 attribute 绑定到
slot
上。这些绑定在<slot>
元素上的 attribute 被称为插槽 prop。同时通过v-slot="自己提供的prop名字"
来使用 - 只有默认插槽时,可以直接在组件的标签上使用
v-slot
v-slot
可以缩写为#
,但是缩写只在其有参数的时候才可用(必须写default才能使用#)
知识点来源于 [vue 官网-插槽内容](插槽 | Vue.js (vuejs.org))
别看内容多,其实也就那么回事!!!