Vue.js 从 2.6.0 版本起,已废弃的使用 slot 属性语法。
有时我们需要多个插槽,例如:对于一个带有如下模板的 <base-layout> 组件,我们需要在 <header>、<main> 和 <footer> 中添加插槽:
<div class="container"> <header> <!-- 我们希望把页头放这里 --> </header> <main> <!-- 我们希望把主要内容放这里 --> </main> <footer> <!-- 我们希望把页脚放这里 --> </footer> </div>
对于这样的情况,站在使用 <base-layout> 组件的角度,我们该如何区分这些插槽呢?幸运的是 <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”。
<base-layout> 组件定义如上,我们该怎样去使用它呢?在向指定名称的插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并通过 v-slot 指令指定具体插槽的名称,如下:
<base-layout> <!-- 这是提供给名为 header 插槽的默认值 --> <template v-slot:header> <h1>Here might be a page title</h1> </template> <!-- 默认插槽,名为 default 插槽的默认值 --> <p>A paragraph for the main content.</p> <p>And another one.</p> <!-- 这是提供给名为 footer 插槽的默认值 --> <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>
上面的任何一种写法都会渲染出如下 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>
注意:v-slot 只能添加在 <template> 上 (只有一种例外情况),这一点和已经废弃的 slot 属性不同。
Vue.js 中 2.6.0 新增。跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header,例如:
<base-layout> <template #header>...</template> <p>A paragraph for the main content.</p> <p>And another one.</p> <template #footer>...</template> </base-layout>
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
<!-- 这样会触发一个警告 --> <current-user #="{ user }"> {{ user.firstName }} </current-user>
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
<current-user #default="{ user }"> {{ user.firstName }} </current-user>
Vue.js 中 2.6.0 新增。动态指令参数也可以用在 v-slot 上,来定义动态的插槽名,例如:
<base-layout> <template v-slot:[dynamicSlotName]> ... </template> </base-layout>
<html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue</title> <!-- 使用 CDN 引入 Vue 库 --> <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> --> <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.js"></script> </head> <body> <div id="app"> <base-layout> <!-- 这是提供给名为 header 插槽的默认值 --> <template v-slot:header> <h1>Here might be a page title</h1> </template> <!-- 默认插槽,名为 default 插槽的默认值 --> <p>A paragraph for the main content.</p> <p>And another one.</p> <!-- 这是提供给名为 footer 插槽的默认值 --> <template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout> </div> <script type="text/javascript"> Vue.component('base-layout', { template: ` <div> <header style="background:red;"> <slot name="header"></slot> </header> <main style="background:green;"> <slot></slot> </main> <footer style="background:blue;"> <slot name="footer"></slot> </footer> </div> ` }); var app = new Vue({ el: "#app", data: {} }); </script> </body> </html>
运行效果如下图: