1、插槽(父组件向子组件分发内容)
内容分发效果
1、在另一个组件A(父组件)中引入B(子组件)
<B>xxx</B>
2、在组件B中
<slot></slot>
插槽默认值:
<slot> xxx </slot> 当不提供任何插槽内容时,将会显示xxx,否则显示传入的内容
插槽作用域:
<B>{{变量}}</B> 变量的作用域是变量定义的地方,而不是B组件内部
具名插槽:
1、即组件B中的slot命名
<slot name='xx'></slot>
<slot></slot> 默认为:<slot name='default'></slot>
2、则A组件要写成
<template v-slot:xx> v-slot只能添加在<template>上(只有一种例外情况),这一点和已经废弃的slot attribute不同。
xxx
</template>
<template> 默认为v-slot:default
xxx
</template>
动态指令参数也可以用在v-slot上,来定义动态的插槽名:
<template v-slot:[dynamicSlotName] 或 #[dynamicSlotName]>
...
</template>
v-slot可缩写为#,#后必须有参数
<template #xx>
...
</template>
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用
<todo-list v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</todo-list>
3、组件B中命名插槽才会显示组件A分发的内容,原slot标签中的内容会被覆盖
向插槽传递数据
内部传递数据:
<span>
<slot :user="user">
{{ user.lastName }} 默认值
</slot>
</span>
父组件接收:
v-slot可以在组件和template上使用
<组件 >
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
可简写为:
(1)v-slot='slotProps'
(2)默认插槽#简写方式必须带上参数default
#default='slotProps'
</组件>
默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
错误:<current-user v-slot="slotProps">
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
解构传递的值:
v-slot本质是一个只有一个参数的函数,该参数是包含了所有传递内容的对象,所以可以进行函数参数的操作
<组件 v-slot="{ user }">
{{ user.firstName }}
</组件>
解构重命名:
<组件 v-slot="{ user: person }">
{{ person.firstName }}
</组件>
设置默认值:
<组件 v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</组件>
通过实例属性获取插槽虚拟节点内容
(1)$slots
获取插槽的内容,会是类似虚拟节点的结构
<组件>
<template v-slot:foo>
具名内容
</template>
啊哈哈
</组件>
具名插槽:this.$slots.foo() v3版本
默认插槽:this.$slots.default()
渲染函数中应用,v2.5版本
<blog-post>
<template v-slot:header>
<h1>About Me</h1>
</template>
<p>Here's some page content, which will be included in vm.$slots.default, because it's not inside a named slot.</p>
<template v-slot:footer>
<p>Copyright 2016 Evan You</p>
</template>
<p>If I have some content down here, it will also be included in vm.$slots.default.</p>.
</blog-post>
Vue.component('blog-post', {
render: function (createElement) {
var header = this.$slots.header
var body = this.$slots.default
var footer = this.$slots.footer
return createElement('div', [
createElement('header', header),
createElement('main', body),
createElement('footer', footer)
])
}
})
(2)$scopedSlots
作用域插槽,包括默认slot在内的每一个插槽,该对象都包含一个返回相应VNode的函数。
所有的$slots现在都会作为函数暴露在$scopedSlots中,如果你在使用渲染函数,不论当前插槽是否带有作用域,我们都推荐始终通过$scopedSlots访问它们,这不仅仅使得在未来添加作用域变得简单,也可以让你最终轻松迁移到所有插槽都是函数的Vue 3。
2、内联模板
在组件上使用inline-template,会将包裹的内容作为组件的template而非插槽
<my-component inline-template>
<div>
<p>These are compiled as the component's own template.</p>
<p>Not parent's transclusion content.</p>
</div>
</my-component>
其中:内联模板需要定义在Vue所属的DOM元素内
3、text/x-template模板
会将script标签内的内容,作为组件模板
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
组件中:
template:'#hello-world-template'
其中:
x-template需要定义在Vue所属的DOM元素外
代码示例:
我们可能希望这个 <button> 内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 <slot> 标签内:
<button type="submit">
<slot>Submit</slot>
</button>
现在当我在一个父级组件中使用 <submit-button> 并且不提供任何插槽内容时:
<submit-button></submit-button>
后备内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
但是如果我们提供内容:
<submit-button>
Save
</submit-button>
则这个提供的内容将会被渲染从而取代后备内容:
<button type="submit">
Save
</button>
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout> 组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot> 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name 的 <slot> 出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
现在 <template> 元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。
然而,如果你希望更明确一些,仍然可以在一个 <template> 中包裹默认插槽的内容:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
任何一种写法都会渲染出:
<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 只能添加在 <template> 上 (只有一种例外情况),这一点和已经废弃的 slot attribute 不同。
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
绑定在 <slot> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。
这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot 被假定对应默认插槽:
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<!-- 无效,会导致警告 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:
function (slotProps) {
// 插槽内容
}
这意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件或现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 user 重命名为 person:
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
你甚至可以定义后备内容,用于插槽 prop 是 undefined 的情形:
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
动态指令参数也可以用在 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>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
<!-- 这样会触发一个警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>