匿名插槽
Vue 实现了一套内容分发的 API,将 slot 元素作为承载分发内容的出口,当组件渲染的时候,slot 将会被替换为子组件标签内的内容,插槽内可以包含任何模板代码,包括 HTML甚至其他组件,如果没有这个 slot 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
Vue.component('MyBtn', {
template: `
<button>
<slot></slot>
</button>
`,
})
const App = {
template: `
<div>
<MyBtn>登录</MyBtn>
<MyBtn>注册</MyBtn>
</div>
`,
}
后备内容
有时为一个插槽设置
默认的内容是很有用的,它只会在没有提供内容的时候被渲染。<button type="submit"> <slot>Submit</slot> </button>
具名插槽
有时我们需要多个插槽,例如对于一个带有如下模板的 base-layout 组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
这种情况下 slot 元素有一个特殊的属性:name,这个属性可以用来定义额外的插槽:
<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 的 tempate 中的内容都会被视为默认插槽的内容
如果希望更明确一些,仍然可以用 <template v-slot:default> 在一个 template 中包裹默认插槽的内容
渲染结果:
<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>
作用域插槽
父级模板里的所有内容都是在父级作用域中编译的,子模板里的所有内容都是在子作用域中编译的
有时让父组件插入的插槽中的内容能够访问子组件中才有的数据,需要用到作用域插槽
为了让 msg 在父级的插槽内容中可用,我们可以将 msg 作为 slot 元素的一个属性绑定上去
绑定在 slot 元素上的属性被称为插槽prop,现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽prop的名字
const msg = {
data() {
return {
msg: '子组件中的数据'
}
},
template: `
<div>
<slot :msg='msg'></slot>//1.在slot标签内,自定义属性绑定子组件中的的msg对象
<h3>作用域插槽</h3>
</div>
`
}
const App = {
components: {
msg,
},
template: `
<msg>
<template v-slot:default='childMsg'>//2.使用带值的v-slot来定义提供的插槽prop的名字
<h2>{{childMsg.msg}}</h2>//3.通过插槽名.子组件slot绑定的属性名来访问子组件中数据
</template>
</msg>
`
}
插槽的缩写写法
独占默认插槽的缩写语法
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上
<msg v-slot:default="childMsg">
{{ childMsg.msg }}
</msg>
这种写法还可以更简单,不带参数的 v-slot 被假定对应默认插槽
<msg v-slot="childMsg">
{{ childMsg.msg }}
</msg>
注意: 默认插槽的缩写语法不能*和具名插槽混用,因为它会导致作用域不明确,不要使用缩写
只要出现多个插槽,请始终为所有的插槽使用完整的基于 template 的语法:
<msg>
<template v-slot:default="childMsg">
{{ childMsg.msg }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</msg>
具名插槽的缩写
把参数之前的所有内容 (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>
和其它指令一样,该缩写只在其有参数的时候才可用,如果你希望使用缩写的话,你必须始终明确插槽名
<msg #default="childMsg">
{{ childMsg.msg }}
</msg>