在 Vue 中,android系统定制开发组件可以递归的调用本身,android系统定制开发但是有一些条件:
- android系统定制开发该组件一定要有
name
属性- android系统定制开发要确保递归的调用有终止条件,android系统定制开发防止内存溢出
不知道大家有没遇到过这样的场景:渲染列表数据的时候,列表的子项还是列表。如果层级少尚且可以用几个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。
以上就是今天的分享!是有点绕,请耐心看完思考并且自己动手试一试。有兴趣的小伙伴可以动手试一哈,把组件进一步封装,或修改成自己想要的样式和效果。