Slot 通俗的理解就是 “占坑” ,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑,替换组件模板中slot位置,并且可以作为承载分发内容的出口。
定义两个组件 :index.vue 和 navLink.vue
然后在index.vue组件中引用navLink.vue组件。
index.vue
<navLink>链接</navLink>
navLink.vue
<div>
<a href="url"><slot></slot></a>
<p>请点击该链接进行跳转</p>
</div>
如下图所示:当组件渲染的时候,slot标签的位置会被替换为 链接。
插槽内可以是普通文本,html标签,组件。
<template>
<navLink>{{link}}</navLink>
</template>
<script>
import navLink from '@/components/slots/navLink'
export default {
name: 'Slot',
data () {
return {
link: '点击'
}
},
components: {
navLink
}
}
</script>
插槽可以获取到index.vue组件里的内容:
如果将插槽渲染的内容更换为navLink.vue里面的变量url:
<template>
<navLink>{{url}}</navLink>
</template>
那么插槽就获取不到url了:
规则:
父级模板里的所有内容都是在父级作用域中编译的;
子模板里的所有内容都是在子作用域中编译的。
有时候我们需要给插槽设置一个具体的默认内容,当别的组件没有提供内容的时候,那么默认的内容就会被渲染。
navLink.vue:在slot插槽里设置默认内容 链接:
<div>
<a href="url"><slot>链接</slot></a>
<p>请点击该链接进行跳转</p>
</div>
在index.vue里直接使用navLink.vue:
<template>
<navLink></navLink>
</template>
那么设置的默认内容 链接 将会被渲染:
假如我们提供内容,那么这个提供的内容将会替代默认的内容被渲染出来:
index.vue:
<template>
<navLink>点击</navLink>
</template>
有时候一个组件里需要多个插槽, 对于这样的情况,slot有一个特殊的特性:name ,这个特性可以用来定义额外的插槽。
下面我们以 index.vue 组件和 page.vue 组件为例,我们会先在 page.vue 中利用 name属性 声明多个插槽,然后再在 index.vue 中引用并插入对应的内容。
page.vue:
<template>
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
在向具名插槽提供内容的时候,我们可以在template元素上使用v-slot指令,并以参数的形式提供其名称。
如果一个slot标签不带name属性的话,那么它的name默认为default,例如main标签中的slot。
index.vue:
<template>
<page>
<template v-slot:header>
This is header
</template>
<template v-slot:default>
This is main
</template>
<template v-slot:footer>
this is footer
</template>
</page>
</template>
我们在编译作用域中讲过,父级模板中的插槽只能访问父级作用域中的变量,如何能让插槽内容访问子组件中的数据呢?
我们把需要传递的内容 绑到slot标签 上,然后在父组件中用 v-slot 设置一个值来定义我们提供插槽的名字。
例如:在子组件 currentUser.vue 中绑定 user 变量
<template>
<div>
<!-- 设置默认值:{{user.lastName}}获取 He -->
<!-- 如果父组件index.vue中给这个插槽值的话,则不显示 He -->
<!-- 把user绑到slot标签上 -->
<slot :user="user">{{user.lastName}}</slot>
</div>
</template>
<script>
export default {
name: 'currentUser',
data () {
return {
user: {
firstName: 'Lu',
lastName: 'He'
}
}
}
}
</script>
绑定在 slot 元素上的特性被称为 插槽 prop。在父组件中,我们可以用 v-slot 设置一个值来定义我们提供的 插槽prop的名字,例如下面的 slotProps,然后直接使用就好了。
index.vue :
<template>
<currentUser v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</currentUser>
</template>
在上述情况下,当被提供的内容只有 默认插槽 时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上,所以上面的代码还可以简化为:
index.vue :
<template>
<!-- 可以把 :default 去掉,仅限于默认插槽 -->
<currentUser v-slot="slotProps">
{{ slotProps.user.firstName }}
</currentUser>
</template>
注意: 默认插槽 的缩写语法不能和 具名插槽 混用,因为它会导致作用域不明确。
<template>
<currentUser v-slot="slotProps">
{{ slotProps.user.firstName }}
<!-- 无效,会警告 -->
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</currentUser>
</template>
只要出现多个插槽,所有的插槽必须使用完整的的语法:
<template>
<currentUser>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</currentUser>
</template>
因为 作用域插槽 的内部工作原理是 将你的插槽内容包括在一个传入单个参数的函数里 ,这意味着 v-slot 的值可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 ,也可以使用 ES2015 解构 来传入具体的 插槽 prop,如下:
<template>
<currentUser v-slot="{user}">
{{ user.firstName }}
</currentUser>
</template>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如可以将 user 重命名为 person:
<template>
<currentUser v-slot="{user:person}">
{{ person.firstName }}
</currentUser>
</template>
甚至可以定义的 后备内容(默认内容),用于插槽没有值时可以使用默认内容的情形:
<template>
<currentUser v-slot="{user={firstName:'Lu'}}">
{{ user.firstName }}
</currentUser>
</template>
动态指令参数也可以用在 v-slot 上来定义 动态的插槽名 :
index.vue:
<template>
<page v-slot:[getSlotName]>
This is header
</page>
</template>
import page from '@/components/slots/page'
export default {
name: 'Slot',
components: {
page
},
computed: {
// 用计算属性定义动态的插槽名
getSlotName () {
return 'header'
}
}
}
</script>
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把 v-slot: 替换为字符 #,例如 v-slot:header 可以被重写为 #header。
我们将具名插槽的例子重新改写下:
<template>
<page>
<template #header>
This is header
</template>
<template>
This is main
</template>
<template #footer>
this is footer
</template>
</page>
</template>
注意:该指令和其他指令一样,只在其有参数的时候才可用。
下面的书写形式是错误的:
<template>
<currentUser #="{user}">
{{ user.firstName }}
</currentUser>
</template>
如果希望使用缩写的话,必须始终以明确插槽名取而代之:
<template>
<currentUser #default="{user}">
{{ user.firstName }}
</currentUser>
</template>
插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。 这在设计封装数据逻辑同时允许父级组件自定义部分布局的可复用组件时是最有用的。
例如,我们要实现一个 todo-list 组件,它是一个列表且包含布局和过滤逻辑:
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
我们可以将每个 todo 作为父级组件的插槽,以此通过父级组件对其进行控制,然后将 todo 作为一个 插槽 prop 进行绑定:
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<!-- 我们为每个 todo 准备了一个插槽, 将 todo 作为一个插槽的 prop 传入。-->
<slot name="todo" :todo="todo">
<!-- 后备内容 -->
{{ todo.text }}
</slot>
</li>
</ul>
现在当我们使用 todo-list 组件的时候,我们可以选择为 todo 定义一个不一样的 template 作为替代方案,并且可以从子组件获取数据:
<todo-list :todos="todos">
<template v-slot:todo="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>
以上就是Vue插槽的全部内容,如有疑问,请参考:官方文档