插槽
Vue2中,插槽的使用可以在父组件中传递结构给子组件,复用子组件的同时也扩展了自定义能力
1. 默认插槽
子组件中(假设子组件name为Chlid):
<template>
<div>
<h1>这是子组件</h1>
<slot>
占位符,父组件的结构可以渲染到这里,当父组件没有提供内容时,这里的文字将被正常渲染,反之,这里的默认内容将被清空
</slot>
</div>
</template>
父组件中:
<template>
<Child>
向子组件传递的内容写到这里,插槽内可以包含任何模板代码
</Child>
</template>
1.1 编译作用域
插槽仅可以访问本模板(父组件)的数据,和父组件有着相同的作用域,无法访问到子组件数据
<Child url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的 `url` 会是 undefined,因为其 (指该插槽的) 内容是_传递给_ <Child> 的,无法直接访问
-->
</Child>
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
2. 具名插槽
当组件使用多个插槽时,为了让插槽内容在合适的地方渲染,<slot>
元素有一个特殊的 attribute: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
的参数的形式提供其名称,如:
父组件:
<Child>
<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>
</Child>
注意 v-slot
只能添加在 <template>
上 (只有一种例外情况)
2.1 具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
<Child>
<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>
</Child>
然而,和其它指令一样,该缩写只在其有参数的时候才可用
3. 作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的,例如,设想一个带有如下模板的 <Child>
组件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
我们可能想换掉备用内容,用名而非姓来显示。如下:
<Child>
{{ user.firstName }}
</Child>
但是由于作用域问题,user在父组件中无法访问,为了让 user
在父级的插槽内容中可用,我们可以将 user
作为 <slot>
元素的一个 attribute 绑定上去:
<span>
<slot :user="user">
{{ user.lastName }}
</slot>
</span>
绑定在 <slot>
元素上的 attribute 被称为插槽 prop。于是,现在在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop 的名字:
<Child>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</Child>
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps
,但你也可以使用任意你喜欢的名字。
3.1 独占默认插槽的缩写语法
在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上:
<Child v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</Child>
再简单点,不带参数的 v-slot
被假定对应默认插槽:
<Child v-slot="slotProps">
{{ slotProps.user.firstName }}
</Child>
只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template>
的语法:
<Child>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</Child>
3.2 解构插槽
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:
function (slotProps) {
// 插槽内容
}
这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数 (slotProps) 的 JavaScript 表达式,可以使用解构赋值来传入具体的插槽 prop,如:
<Child v-slot="{ user }">
{{ user.firstName }}
</Child>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 user
重命名为 person
:
<Child v-slot="{ user:person }">
{{ user.firstName }}
</Child>
3.3 动态插槽名
动态指令参数也可以用在 v-slot
上,来定义动态的插槽名:
<Child>
<template v-slot:[dynamicSlotName]>
...
</template>
<Child>
这里的 dynamicSlotName
会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data
property dynamicSlotName
,其值为 "href"
,那么这个绑定将等价于 v-slot:href
。