slot - 插槽的使用

插槽

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>

你甚至可以定义后备内容,用于插槽 propundefined 的情形:

	<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 动画替代)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值