Vue 实现了一套内容分发的 API,这套 API 基于当前的 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。
它允许你像这样合成组件:
<div> <navigation-link url="/profile"> Your Profile </navigation-link> </div>
然后你在 <navigation-link> 的模板中可能会写为:
Vue.component('navigation-link', { template: ` <a v-bind:href="url" > <slot></slot> </a> ` });
当组件渲染的时候,这个 <slot> 元素将会被替换为“Your Profile”。
插槽内可以包含任何模板代码,包括 HTML:
<navigation-link url="/profile"> <!-- 添加一个 Font Awesome 图标 --> <span></span> Your Profile </navigation-link>
甚至其它的组件:
<navigation-link url="/profile"> <!-- 添加一个图标的组件 --> <font-awesome-icon></font-awesome-icon> Your Profile </navigation-link>
如果 <navigation-link> 没有包含一个 <slot> 元素,则任何传入它的内容都会被抛弃。
2 具名插槽
有些时候我们需要多个插槽。例如,一个假设的 <base-layout> 组件多模板如下:
<div> <header> <!-- 我们希望把页头放这里 --> </header> <main> <!-- 我们希望把主要内容放这里 --> </main> <footer> <!-- 我们希望把页脚放这里 --> </footer> </div>
对于这样的情况,<slot> 元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:
<div> <header> <slot></slot> </header> <main> <slot></slot> </main> <footer> <slot></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's 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's some contact info</p> </base-layout>
我们还是可以保留一个未命名插槽,这个插槽是默认插槽,也就是说它会作为所有未匹配到插槽的内容的统一出口。上述两个示例渲染出来的 HTML 都将会是:
<div> <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>
3 默认插槽的内容
有的时候为插槽提供默认的内容是很有用的。例如,一个 <submit-button> 组件可能希望这个按钮的默认内容是“Submit”,但是同时允许用户覆写为“Save”、“Upload”或别的内容。
你可以在 <slot> 标签内部指定默认的内容来做到这一点。
<button type="submit"> <slot>Submit</slot> </button>
如果父组件为这个插槽提供了内容,则默认的内容会被替换掉。
4 编译作用域
当你想在插槽内使用数据时,例如:
<navigation-link url="/profile"> Logged in as {{ user.name }} </navigation-link>
该插槽可以访问跟这个模板的其它地方相同的实例属性 (也就是说“作用域”是相同的)。但这个插槽不能访问 <navigation-link> 的作用域。例如尝试访问 url 是不会工作的。牢记一条准则:
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
5 作用域插槽
2.1.0+ 新增
有的时候你希望提供的组件带有一个可从子组件获取数据的可复用的插槽。例如一个简单的 <todo-list> 组件的模板可能包含了如下代码:
Vue.component('todo-list',{ template:` <ul> <li v-for="todo in todos" v-bind:key="todo.id"> {{ todo.text }} </li> </ul> ` });
但是在我们应用的某些部分,我们希望每个独立的待办项渲染出和 todo.text 不太一样的东西。这也是作用域插槽的用武之地。