框架实战指南-访问孩子

26 阅读3分钟

本文是系列文章的一部分:框架实战指南 - 基础知识

“传递子项”一章中,我们讨论了如何将组件和元素作为子项传递给另一个组件:

<FileTableContainer>	<FileTableHeader />	<FileTableBody /></FileTableContainer>

我们还谈到了访问以下内容的能力:

但仅当相应的 HTML 元素或组件位于父模板本身内部时:

// Inside FileTableContainer
<FileTableHeader /><FileTableBody />

相反,如果有一种方法可以通过插槽区域逐个访问传递给元素的子元素,那会怎样?

那么,有办法吗?

是的。

让我们从一个简单的例子开始:计算一个组件有多少个子组件。

计算组件的子组件数量

让我们计算一下有多少元素和组件被传递给我们的组件:

与 React 和 Angular 不同,Vue 的 API 不允许我们轻松地统计子列表项的数量。造成这种情况的原因有很多,但我们会在本系列的第三本书中从头重写 Vue时尽力解释。

相反,您需要将列表从父组件传递到列表显示以显示您想要的值:

<!-- ParentList --><script setup>const props = defineProps(["list"]);</script><template>	<p>There are {{ props.list.length }} number of items in this array</p>	<ul>		<slot></slot>	</ul></template>
<!-- App.vue --><script setup>import ParentList from "./ParentList.vue";const list = [1, 2, 3];</script><template>	<ParentList :list="list">		<li v-for="i in list">Item {{ i }}</li>	</ParentList></template>

循环渲染子元素

现在我们已经熟悉了如何访问子组件,让我们使用之前介绍过的相同 API 来获取以下组件模板:

<ParentList>	<span>One</span>	<span>Two</span>	<span>Three</span></ParentList>

呈现以下 HTML:

<ul>	<li><span>One</span></li>	<li><span>Two</span></li>	<li><span>Three</span></li></ul>

与之前一样,Vue 的 API 在访问直接子组件时存在限制。我们将在本系列丛书的第三部分探讨原因 。

动态添加子项

现在我们有了由组件转换的项目列表,让我们添加将另一个项目添加到列表中的功能:

虽然 Vue 无法渲染列表中的子元素,但它还有更多功能值得展示。亲爱的读者,请继续阅读。

在这里,我们可以看到,每当一个随机数添加到列表中时,我们的列表项计数器仍然会正确递增。

将值传递给投影内容

虽然计算列表中项目的数量很新颖,但在 JavaScript 中访问投影内容并不是一种非常实用的用途。

相反,我们来看看有没有办法将值传递给投影内容。例如,尝试在li索引为偶数或奇数时更改每个项目的背景颜色。

Vue 可以……等等!Vue可以做到!

让我们从本章开始的地方拿出代码并重构它,这样我们就不必v-for在 里面放置我们的代码了App.vue。相反,让我们将它移入元素ParentList.vue并将属性传递给它<slot>

<!-- ParentList.vue --><script setup>const props = defineProps(["list"]);</script><template>	<p>There are {{ props.list.length }} number of items in this array</p>	<ul id="parentList">		<slot			v-for="(item, i) in props.list"			:item="item"			:i="i"			:backgroundColor="i % 2 ? 'grey' : ''"		></slot>	</ul></template>
<!-- App.vue --><script setup>import { ref } from "vue";import ParentList from "./ParentList.vue";const list = ref([1, 2, 3]);function addOne() {	const randomNum = Math.floor(Math.random() * 100);	list.value.push(randomNum);}</script><template>	<ParentList :list="list">		<!-- Think of this as "template is receiving an object		 we'll call props" from "ParentList" -->		<template v-slot="props">			<li :style="'background-color:' + props.backgroundColor">				{{ props.i }} {{ props.item }}			</li>		</template>	</ParentList>	<button @click="addOne()">Add</button></template>

v-slot类似于将属性传递给组件的方式,但我们将数据直接传递给要template由其渲染的组件v-slot

您可以对使用情况进行对象解构v-slot,以访问属性名称,而不必props每次都重复:

<template v-slot="{item, i}">	<li>{{i}} {{item}}</li></template>

挑战复制链接

让我们写一个表格组件吧!像这样:

标题一标题二
一些值 1一些值 2
一些值 3一些值 4
一些值 5一些值 6

但是,我们不必自己编写 HTML,而是尝试让开发团队轻松使用此表。

让我们通过:

  • 对象数据数组
  • 接收对象数据长度的表头模板
  • 接收每行数据的值的表体模板

这样,我们的App组件中就不需要任何循环了。

<!-- App.vue --><script setup>import Table from "./Table.vue";const data = [	{		name: "Corbin",		age: 24,	},	{		name: "Joely",		age: 28,	},	{		name: "Frank",		age: 33,	},];</script><template>	<Table :data="data">		<template #header="{ length }">			<tr>				<th>{{ length }} items</th>			</tr>		</template>		<template v-slot="{ rowI, value }">			<tr>				<td					:style="rowI % 2 ? 'background: lightgrey' : 'background: lightblue'"				>					{{ value.name }}				</td>			</tr>		</template>	</Table></template>
<!-- Table.vue --><script setup>const props = defineProps(["data"]);</script><template>	<table>		<thead>			<slot name="header" :length="props.data.length"></slot>		</thead>		<tbody>			<slot				v-for="(item, i) in props.data"				:key="i"				:rowI="i"				:value="props.data[i]"			/>		</tbody>	</table></template>