本文是系列文章的一部分:框架实战指南 - 基础知识
在“传递子项”一章中,我们讨论了如何将组件和元素作为子项传递给另一个组件:
<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>