插槽内容
Vue实现一套内容分发的API, 这套API基于当前的web组件规范草案,将<slot>元素作为承载分发内容的出口。
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后在<navigation-link>的模板中可能会写为:
<a :href="url" class="nav-link">
<slot></slot>
</a>
当组件渲染的时候,这个<slot>元素将会被”Yourt Profile”替换。插槽可以包括任何模板代码,包括HTML:
<navigation-link url="/profile">
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
甚至其他的组件:
<navigation-link url="/profile">
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
如果<navigation-link>没有包含一个<slot>元素,则任何传入它的内容都会被抛弃。
具名插槽
有些时候我们需要多个插槽,例如:
<div>
<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>
在向具名插槽提供内容的时候,我们可以在一个父组件的<template>元素上使用slot特性:
<base-layout>
<template 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 slot="footer">
<p>here is some contact info</p>
</template>
</base-layout>
另一种slot特性的用法是直接用在一个普通元素上:
<base-layout>
<h1 slot="header">Here might be a page title</h1>
<p>A paragraph for the main content</p>
<p>And another one</p>
<p slot="footer">Here is some contact info</p>
</base-layout>
我们还是可以保留一个未命名插槽,这个插槽是默认插槽,也就是说它会作为所有未匹配到插槽的内容的统一出口,上述两个示例渲染出来的HTML都将会是:
<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>
插槽的默认内容
有的时候为插槽提供默认内容是很有用的。例如,一个<submit-button>组件可能希望这个按钮的默认内容是”Submit”,但是同时允许用户覆盖”Save”,”Upload”或者别的内容。
你可以在组件模板里的<slot>标签内部指定默认的内容来做到这一点。
<button type="submit">
<slot>Submit</slot>
</button>
如果父组件为这个插槽提供内容,则默认内容会被替换。
编译作用域
当你想在插槽内使用数据时,例如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
该插槽可以访问跟这个模板的其他地方相同的实例属性(也就是说作用域相同)。但这个插槽不能访问<navigation-link>的作用域。例如尝试访问URL是不会工作的。牢记:
>**父组件模板的所有东西都会在父级作用域内编译;子组件的模板的所有东西都会在子级作用域内编译。**
作用域插槽
有时候你希望提供的组件带有一个可从子组件获取数据的可复用的插槽:
<ul>
<li v-for="todo in todos" v-bind="todo.id">
{{ todo.text }}
</li>
</ul>
但是在我们应用的某些部分,我们希望每个独立的代办项渲染出和todo.text不太一样的东西。这也是作用域插槽的用武之地。
为了让这个特性成为可能,你需要做的全部事情就是将代办项内容包裹在一个<slot>元素上,然后将所有和其上下文有关的数据传递给这个插槽:在这个例子中,这个数据是todo对象:
<ul>
<li v-for="todo in todos" :key="todo.id">
<slot :todo="todo">{{ todo.text }}</slot>
</li>
</ul>
现在当我们使用<todo-list>组件的适合,我们可以选择为代码项定义一个不一样的<template>作为替代方案,并且可以通过slot-scope特性从子组件获取数据:
<todo-list :todos="todos">
<!-- 将 `slotProps` 定义为插槽作用域的名字 -->
<template slot-scope="slotProps">
<!-- 为待办项自定义一个模板,-->
<!-- 通过 `slotProps` 定制每个待办项。-->
<span v-if="slotProps.todo.is">good</span>
{{ slotProps.todo.text }}
</template>
</todo-list>
解构 slot-scope
如果一个JavaScript表达式在一个函数定义的参数位置有效,那么这个表达式实际上就可以被slot-scope接受。也就是说你可以在支持的环境下,在这些表达式中使用ES2015解构语法。
<todo-list :todos="todos">
<template slot-scope="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>