插槽
Vue 实现了一套内容分发的 API,将 slot
元素作为承载分发内容的出口。
它允许你像这样合成组件:
<template>
<view>
<componentA>
Your Profile
</componentA>
</view>
</template>
在 <componentA>
的模板中可能会写为:
<template>
<view>
<!-- 我是子组件componentA -->
<view >{{title}}</view>
<slot></slot>
</view>
</template>
当组件渲染的时候,<slot></slot>
将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML
:
<template>
<view>
<!-- 我是父组件 -->
<componentA>
<view>Your Profile</view>
<!-- 添加一个 uni-icons 图标 -->
<uni-icons type="contact" size="30"></uni-icons>
</componentA>
</view>
</template>
如果 <componentA>
的 template
中没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
编译作用域
当你想在一个插槽中使用数据时,例如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
该插槽跟模板的其它地方一样可以访问相同的实例 property
(也就是相同的“作用域”),而不能访问 <navigation-link>
的作用域。例如 url
是访问不到的:
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的 `url` 会是 undefined,
因为其 (指该插槽的) 内容是_传递给_ <navigation-link> 的
而不是在 <navigation-link> 组件*内部*定义的。
-->
</navigation-link>
记住规则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
默认内容
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button>
组件中:
<button type="submit">
<slot></slot>
</button>
我们可能希望这个 <button>
内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 <slot>
标签内:
<button type="submit">
<slot>Submit</slot>
</button>
- 当我在一个父级组件中使用
<submit-button>
并且不提供任何插槽内容时:
<!-- 父级组件:不提供任何插槽内容-->
<submit-button></submit-button>
<!-- 子组件:后备内容 “Submit” 将会被渲染: -->
<button type="submit">
Submit
</button>
- 当我在一个父级组件中使用
<submit-button>
并且提供插槽内容时:
<!-- 父级组件:提供插槽内容-->
<submit-button>
Save
</submit-button>
<!-- 子组件:则这个提供的内容将会被渲染从而取代后备内容: -->
<button type="submit">
Save
</button>
具名插槽
需要多个插槽时,可以利用 <slot>
元素的一个特殊的特性:name
来定义具名插槽
<base-layout>
子组件模板:
<template>
<view class="container">
<header>
<!-- 我们希望把页头放这里 -->
<slot name="header"></slot>
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
<slot></slot>
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
<slot name="footer"></slot>
</footer>
</view>
</template>
- 在向具名插槽提供内容的时候,我们可以在一个
<template>
元素上使用v-slot
指令,并以 v-slot 的参数的形式提供其名称:
<template>
<view>
<!-- 父组件使用子组件`<base-layout>`,节点上使用slot特性: -->
<base-layout>
<template v-slot:header>
<view>Here might be a page title</view>
</template>
<template v-slot:default>
<view>A paragraph for the main content.</view>
<view>And another one.</view>
</template>
<template v-slot:footer>
<view>Here's some contact info</view>
</template>
</base-layout>
</view>
</template>
现在 <template>
元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template>
中的内容都会被视为默认插槽的内容。
v-slot 只能添加在
<template>
上一个不带 name 的
<slot>
出口会带有隐含的名字 “default” 。
具名插槽的缩写
跟 v-on 和 v-bind 一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header
可以被重写为 #header
:
<template>
<view>
<!-- 父组件使用子组件`<base-layout>`,节点上使用slot特性: -->
<base-layout>
<template #header>
<view>Here might be a page title</view>
</template>
<view>A paragraph for the main content.</view>
<view>And another one.</view>
<template #footer>
<view>Here's some contact info</view>
</template>
</base-layout>
</view>
</template>
作用域插槽
在作用域插槽内,父组件可以拿到子组件的数据。子组件可以在 slot
标签上绑定属性值。
有时让插槽内容能够访问子组件中才有的数据是很有用的。
<!-- 子组件 <current-user>-->
<template>
<view>
<slot>{{ user.lastName }}</slot>
</view>
</template>
我们可能想换掉默认内容,用名而非姓来显示。如下:
<template>
<view>
<current-user>
{{ user.firstName }}
</current-user>
</view>
</template>
然而上述代码不会正常工作,因为只有 <current-user>
组件可以访问到 user 而我们提供的内容是在父级渲染的。
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot>
元素的一个 attribute
绑定上去:
<!-- 子组件 <current-user>-->
<template>
<view>
<slot :user="user">{{user.lastName}}</slot>
</view>
</template>
<script>
export default {
data() {
return {
user:{
"lastName":"bar",
"firstName":"foo"
}
}
}
}
</script>
绑定在 <slot>
元素上的 attribute
被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop
的名字:
<template>
<view>
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
</view>
</template>
在这个例子中,我们选择将包含所有插槽 prop
的对象命名为 slotProps
,但你也可以使用任意你喜欢的名字。
独占默认插槽的缩写语法
就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot
被假定对应默认插槽:
<template>
<view>
<current-user>
<template v-slot="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
</view>
</template>
默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<template>
<view>
<!-- 无效,会导致警告 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
</view>
</template>
只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template>
的语法:
<template>
<view>
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</current-user>
</view>
</template>
解构插槽 Prop
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:
function (slotProps) {
// 插槽内容
}
这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript
表达式。
所以在支持的环境下 ( 单文件组件 或 现代浏览器),你也可以使用 ES2015 解构 来传入具体的插槽 prop
,如下:
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop
的时候。它同样开启了 prop
重命名等其它可能,例如将 user
重命名为 person
:
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
你甚至可以定义后备内容,用于插槽 prop
是 undefined
的情形:
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
小程序不支持列表
- 作用域插槽(HBuilderX 3.1.19 以下仅支持解构插槽且不可使用作用域外数据以及使用复杂的表达式)
- 动态组件
- 异步组件
inline-template
X-Templates
keep-alive
(App端也未支持)transition
(可使用animation
或 CSS 动画替代)