作者:Michael Thiessen
译者:前端小智
来源:medium
点赞再看,养成习惯
本文
GitHub
https://github.com/qq44924588… 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎 Star 和完善,大家面试可以参照考点复习,希望我们一起有点东西。
最近我弄清楚了如何递归地实现嵌套插槽,包括如何使用作用域插槽来实现。起因是我想看看是否可以构建一个复制 v-for
指令但仅使用 template
组件。
它还支持插槽和作用域插槽,也可以支持命名插槽,我们可以这样使用它:
<template>
<div>
<!-- Regular list -->
<v-for :list="list" />
<!-- List with bolded items -->
<v-for :list="list">
<template v-slot="{item}">
<strong>{{item}}</strong>
</template>
</v-for>
</div>
</template>
第一个将正常打印列表,而第二个将每个项包装在 <strong>
标记中。
这不是一个非常有用的组件,但可以从中学到的最多,我们来看看。
无循环实现循环
通常,当我们要渲染元素或组件的列表时,可以使用 v-for
指令,但这次我们希望完全摆脱它。
那么,我们如何在不使用循环的情况下渲染项目列表呢?就是使用 递归。
我们可以使用递归来渲染项目列表。过程并不会复杂,我们来看看怎么做。
递归表示一个列表
我在大学里最喜欢的课程之一是“编程语言概念”。
对我来说,最有趣的部分是探索函数式编程和逻辑编程,并了解与命令式编程的区别(Javascript 和最流行的语言是命令式编程)。
这门课让我真正了解如何使用递归,因为在纯函数语言中,一切都是递归。不管怎样,从那门课我学到了可以使用递归地表示一个列表。
与使用数组不同,每个列表是一个值 (头) 和另一个列表(尾)。
[head, tail]
例如要表示列表[1、2、3]
,则可以递归方式表示为:
[1, [2, [3, null]]]
我们必须以某种方式结束列表,因此我们使用 null
而不是另一个数组(也可以使用空数组)。
看到这里,你或许就可以明白了,我们可以使用此概念并将其应用于我们的组件。相反,我们将递归嵌套组件以表示列表。
我们最终将渲染出这样的内容。注意我们“list”的嵌套结构:
<div>
1
<div>
2
<div>
3
</div>
</div>
</div>
诚然,这与 v-for
渲染的效果并不完全相同,但这也不是本练习的重点。
构建组件
首先,我们将解决递归渲染项目列表的问题。
使用递归来渲染列表
这次我们使用一个普通数组,而不是使用前面介绍的递归列表:
[1, 2, 3]
这里要讨论两种情况:
- 基本情形 - 渲染列表中的第一项
- 递归情形 - 渲染项目,然后沉浸下一个列表
我们把 [1,2,3]
传给v-for
<template>
<v-for :list="[1, 2, 3]" />
</template>
我们希望获取列表中的第一项,即1
,并显示它
<template>
<div>
{{list[0] }}
</div>
</template>
现在,该组件将渲染1
,就像我们期望的那样。
但是我们不能只渲染第一个值并停止。我们需要渲染值,然后还渲染列表的其余部分:
<template>
<div>
{{list[0] }}
<v-for :list="list.slice(1)" />
</div>
</template>
我们不传递整个 list
数组,而是删除第一项并传递新数组。第一个项目我们已经打印出来了,所以没有必要保留它。
顺序是这样的:
- 我们将
[1,2,3]
传递到v-for
中进行渲染 - 我们的
v-for
组件渲染1
,然后将[2,3]
传递到下一个v-for
进行渲染 - 取
[2,3]
并渲染2
,然后将[3]
传递到下一个v-for
- 最后一个
v-for
组件渲染出3
,我们已经打印出列表!
现在,我们的 Vue 应用程序的结构如下所示:
<App>
<v-for>
<v-for>
<v-for />
</v-for>
</v-for>
</App>
可以看到,我们有几个 v-for
组件,它们彼此嵌套在一起。最后一件事,我们需要停止递归
<template>
<div>
{{list[0] }}
<v-for
v-if="list.length > 1"
:list="list.slice(1)"
/>
</div>
</template>
最终,渲染完所有项后,我们需要停止递归操作。
递归嵌套的插槽
现在,组件可以正常工作,但是我们也希望它与作用域内插槽一起使用,因为这样可以自定义渲染每个项的方式:
<template>
<v-for :list="list">
<template v-slot="{item}">
<strong>{{item}}</strong>
</template>
</v-for>
</template>
嵌套插槽
一旦弄清楚了如何递归地嵌套插槽,就会对它痴迷一样的感叹:
- 嵌套 n 级的插槽
- 递归插槽
- 包装组件将一个插槽转换为多个插槽
首先,我们将简要介绍嵌套插槽的工作方式,然后介绍如何将它们合并到 v-for
组件中。
假设我们有三个组件:Parent
、Child
和 Grandchild
。我们希望传递来自Parent
组件的一些内容,并在 Grandchild
组件中渲染它。
从 Parent
开始,我们传递一些内容:
// Parent.vue
<template>
<Child>
<span>Never gonna give you up</span>
</Child>
</template>
我们在 Child
组件中做一些事情,将在稍后介绍。然后我们的 Grandchild
组件获取插槽并渲染内容:
// Grandchild.vue
<template>
<div>
<slot />
</div>
</template>
那么,这个 Child
组件是什么样的?
我们需要它从 Parent
组件获取内容并将其提供给 Grandchild
组件,因此我们将两个不同的插槽连接在一起。
// Child.vue
<template>
<Grandchild>
<slot />
</Grandchild>
</template>
请记住,<slot />
元素渲染出作为插槽传递到组件的内容。因此,我们将从 “Parent”
中获取该内容,然后将其渲染到 “Grandchild”
插槽中。
添加作用域插槽
与嵌套作用域插槽唯一不同的是,我们还必须传递作用域数据。将其添加到 v-for
中,我们现在得到以下信息:
<template>
<div>
<slot v-bind:item="list[0]">
<!-- Default -->
{{list[0] }}
</slot>
<v-for
v-if="list.length > 1"
:list="list.slice(1)"
>
<!-- Recursively pass down scoped slot -->
<template v-slot="{item}">
<slot v-bind:item="item" />
</template>
</v-for>
</div>
</template>
首先让我们看一下基本情况。
如果没有提供插槽,则默认 <slot>
元素内部的内容,并像以前一样渲染 list[0]
。但是如果我们提供了一个slot
,它会将其渲染出来,并通过slot
作用域将列表项传递给父组件。
这里的递归情况类似。如果我们将插槽传递给 v-for
,它将在下一个v-for
的插槽中进行渲染,因此我们得到了嵌套。它还从作用域槽中获取 item
并将其传递回链。
现在,我们这个组件仅使用 template
就能实现 v-for
效果。
总结
我们做了很多事情,终于了解了如何创建一个仅使用 template 就能实现 v-for
的效果。
本文主要内容:
- 递归地表示列表
- 递归组件
- 嵌套槽和嵌套作用域槽
原文:https://stackoverflow.com/que…
代码部署后可能存在的 BUG 没法实时知道,事后为了解决这些 BUG,花了大量的时间进行 log 调试,这边顺便给大家推荐一个好用的 BUG 监控工具 Fundebug。
交流
文章每周持续更新,可以微信搜索「大迁世界」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎 Star 和完善,大家面试可以参照考点复习,另外关注公众号,后台回复 福利,即可看到福利,你懂的。