在 Vue 中,组件可以递归的调用本身,但是有一些条件:
- 该组件一定要有
name属性- 要确保递归的调用有终止条件,防止内存溢出
不知道大家有没遇到过这样的场景:渲染列表数据的时候,列表的子项还是列表。如果层级少尚且可以用几个for循环搞定,但是层级多或者层级不确定就有点无从下手了。
其实这就是树形结构数据,像常见的例如导航、空间或逻辑组织、页面定位、级联选择等,其结构可展开或折叠,都属于这种结构。

以上就是使用组件递归,并加入简单交互的展示效果。点击节点会在控制台输出节点对应的数据,如果有子节点,则会展开或收起子节点。接下来我们就看看如何实现以上效果吧!
渲染数据这一步非常简单,首先是把树形结构封装成一个列表组件,其次判断每一项有没有子节点,如果有子节点,再使用自身组件去渲染就可以了。
递归组件:src/components/tree-folder.vue
//项目用到vant-ui库
<template>
<div class="tree-item">
<div v-for="item in treeData" :key="item.id">
<div class="item-title">
<span v-text="item.name"></span>
<span v-if="item.children && item.children.length">
//vant组件库图标 看个人需求换成自己需要的
<van-iconname="arrow-down"/>
</span>
</div>
<div v-if="item.children && item.children.length" class="item-childen">
<tree-folder :treeData="item.children"></tree-folder>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TreeFolder',
props: {
treeData: {
type: Array,
default: () => []
}
}
}
</script>
<style lang="stylus">
.tree-item
.item-title
padding: 4px 8px
.name
color: #000
.negative-rotate
transform: rotate(-180deg)
.item-childen
padding-left: 20px
</style>
父组件: src/home.vue
<template>
//引用递归组件,并传递数据
<TreeFolder :treeData="treeData" />
</template>
<script>
const treeData = [
{ id: 1, name: '一级1' },
{
id: 2,
name: '一级2',
children: [
{ id: 3, name: '二级2-1' },
{ id: 4, name: '二级2-2' }
]
},
{
id: 5,
name: '一级3',
children: [
{
id: 6,
name: '二级3-1',
children: [
{ id: 7, name: '三级3-1-1' },
{ id: 8, name: '三级3-1-2' }
]
},
{ id: 9, name: '二级3-2' },
{ id: 10, name: '二级3-3' }
]
}
]
import TreeFolder from '@/components/tree-folder.vue'
export default {
components: {
TreeFolder
},
data() {
return {
treeData: treeData
}
}
}
</script>

接下来我们要做的是,点击节点时在控制台输出对应的数据。首先我们使用
$emit,将一级节点的 item 传递出去,也就是子传父的方法,相信大家都会。
其次是将内层节点的数据传递出去,同样使用子传父的方法,只是我们需要给组件里面的 tree-folder绑定
@tree-node-click="$emit('tree-node-click', $event)",这样每次子级每次都可以调用父级的 tree-node-click 方法,父级又调用它的父级 tree-node-click 方法,最终调的都是最外层的 tree-node-click 方法,我们只需要在这个过程中,把数据传递过去就可以了。这块有点绕,相信大家多看几遍应该可以看懂。修改如下:
递归组件:src/components/tree-folder.vue
//方法名可以自取 不一定非是'tree-node-click'
<div class="item-title">
<span v-text="item.name"></span>
<span v-if="item.children && item.children.length">
//vant组件库图标 看个人需求换成自己需要的
<van-icon name="arrow-down"/>
</span>
</div>
<div v-if="item.children && item.children.length" class="item-childen">
<tree-folder
:treeData="item.children"
@tree-node-click="$emit('tree-node-click', $event)"
></tree-folder>
</div>
...
methods: {
itemNodeClick(item) {
this.$emit('tree-node-click', item)
}
}
父组件: src/home.vue
<TreeFolder:tree-data="treeData" @tree-node-click="nodeClick" />
...
methods: {
nodeClick(val) {
console.log(val)
}
}

动态展开收起的思路是给组件设置一个数组,数组中存放的是当前列表中需要展开的节点的id,当点击节点的时候添加或删除节点id,然后判断每个节点的id在不在这个数组,在则显示子节点,不在则隐藏子节点。
点击项添加激活样式的思路是给组件绑定一个变量,变量存放的值是点击当前列表中节点的id,当点击节点的时候通过id添加相对应的样式。
图标样式的思路是根据数组中存放的的节点的id,判断节点的id如果在这个数组,则旋转 -180deg,不在则回到最初状态。
递归组件:src/components/tree-folder.vue
//项目用到vant-ui库
<template>
<div class="tree-item">
<div v-for="item in treeData" :key="item.id">
<div class="item-title">
<span
:class="{ 'fc-theme': item.id == curNameId }" // fc-theme激活样式的类名
v-text="item.name">
</span>
<span v-if="item.children && item.children.length">
//vant组件库图标 看个人需求换成自己需要的
<van-icon
name="arrow-down"
style="transition: transform 0.3s" // 加一个延迟动画效果
:class="{ 'negative-rotate': isOpen(item.id), 'fc-theme': item.id ==
curNameId }"/> // 根据id判断是否旋转图标和添加激活样式
</span>
</div>
<div v-if="item.children && item.children.length" v-show="isOpen(item.id)"
class="item-childen">
//这里是重点:current="curNameId",否则无法实现动态添加激活样式
<tree-folder :treeData="item.children" :current="curNameId" @tree-node-
click="$emit('tree-node-click', $event)">
</tree-folder>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TreeFolder',
props: {
treeData: {
type: Array,
default: () => []
},
current: Number //保存当前点击节点的id
},
data() {
return {
expandedKeys: [], // 当前列表需要展开的节点id组成的数组
curNameId: 0 //保存current的值
}
},
computed: {
isOpen() {
return function (id) {
// 判断节点id在不在数组中,在则显示,不在则隐藏
return this.expandedKeys.includes(id)
}
}
},
watch: {
//监听当前点击节点的id
current: {
handler(num) {
this.curNameId = num
},
deep: true,
immediate: true
}
},
methods: {
itemNodeClick(item) {
this.$emit('tree-node-click', item)
if (item.children && item.children.length) {
let index = this.expandedKeys.indexOf(item.id)
if (index > -1) {
// 如果当前节点id存在数组中,则删除
this.expandedKeys.splice(index, 1)
} else {
// 如果当前节点id不存在数组中,则添加
this.expandedKeys.push(item.id)
}
}
}
}
}
</script>
<style lang="stylus">
.tree-item
.item-title
padding: 4px 8px
.name
color: #000
.negative-rotate
transform: rotate(-180deg)
.item-childen
padding-left: 20px
</style>
父组件: src/home.vue
<TreeFolder:tree-data="treeData" :current="curNameId" @tree-node-click="nodeClick" />
...
data() {
return {
curNameId: 0
}
},
methods: {
nodeClick(val) {
this.curNameId = val.id
this.$set(val, 'curNameId', this.curNameId)
}
}

组件调用组件自己的时候,$emit了tree-node-click事件,这个功能主要是做什么的,还有为什么$emit('tree-node-click', $event)第二个参数是$event而不是一个可变数据
这是为了实现内层的子传父功能,使用了@tree-node-click="$emit('tree-node-click', $event)",在内层执行this.$emit("tree-node-click", item) 时候,其实就是执行父组件的 $emit('tree-node-click', $event),然后又会执行爷爷组件的 $emit('tree-node-click', $event),最终执行的是父组件: src/home.vue 的 nodeClick 事件,其实参数一直是item。
以上就是今天的分享!是有点绕,请耐心看完思考并且自己动手试一试。有兴趣的小伙伴可以动手试一哈,把组件进一步封装,或修改成自己想要的样式和效果。
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在编写Ruby(客户端脚本)时,我看到了三种构建更长字符串的方法,包括行尾,所有这些对我来说“闻起来”有点难看。有没有更干净、更好的方法?变量递增。ifrender_quote?quote="NowthatthereistheTec-9,acrappyspraygunfromSouthMiami."quote+="ThisgunisadvertisedasthemostpopularguninAmericancrime.Doyoubelievethatshit?"quote+="Itactuallysaysthatinthelittlebookthatcomeswithit:themo
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现