• vue和uni-app的递归组件排坑


    有这样一个数组数据,实际可能有很多级。

    tree: [
      {
        id: 1,
        name: '1',
        children: [
          { 
            id: 2, 
            name: '1-1', 
            children: [
              {
                id: 7, 
                name: '1-1-1',
                children: []
              }
            ]
          },
          { id: 3, name: '1-2' }
        ]
      },
      {
        id: 4,
        name: '2',
        children: [
          { id: 5, name: '2-1' },
          { id: 6, name: '2-2' }
        ]
      }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    要渲染为下面这种树形

    应该很容易想到使用递归组件,下面就来写一个tree组件。简单用vue2实现一下。

    用法很简单

    传入tree数据,支持返回当前点击的节点。

    实现一下

    先写一个tree组件,在components/tree/index.vue

    <template>
      <div>
        <div 
          class="node" 
          v-for="node in data" 
          :key="node.id"
        >
          <tree-node 
            :node="node" 
            @node-click="nodeClick"
          >
          tree-node>
        div>
      div>
    template>
    
    <script>
      import treeNode from './tree-node.vue'
      export default {
        components: { treeNode },
        props: {
          data: {
            type: Array,
            default: () => []
          }
        },
        methods: {
          nodeClick(node) {
            this.$emit('node-click', node)
          }
        }
      }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    因为父组件页面中传进来的是个数组,需要先循环这个数组,然后递归渲染每一个子节点,我们就再写一个tree-node组件,专门用来渲染子节点,核心代码递归也是在这个组件中完成的。

    同级目录写一个tree-node组件,在components/tree/tree-node.vue

    <template>
      <div>
        
        <div 
          class="node-name" 
          @click.stop="nodeClick"
        >
          {{node.name}}
        div>
        
        <div 
          class="children" 
          v-if="node.children && node.children.length"
        >
          <tree-node 
            v-for="item in node.children" 
            :key="item.id" 
            :node="item" 
            v-on="$listeners"
          >
          tree-node>
        div>
      div>
    template>
    
    <script>
      export default {
        name: 'tree-node',
        props: {
          node: Object
        },
        methods: {
          nodeClick() {
            // 因为传入当前组件的是每一级的节点
            // 只需要再传回去就好了
            this.$emit('node-click', this.node)
          }
        }
      }
    script>
    
    <style scoped>
      .node-name {
        cursor: pointer;
      }
      .children {
        padding-left: 20px;
      }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    划重点、抛出问题

    1. 递归的核心是自己调用自己,但要注意边界条件,否则会进入死循环,内存泄漏。对应到以上代码就是v-if="node.children && node.children.length"
    2. 以上代码已经实现了递归渲染,但只是vue2的实现。你可能注意到其中有一个很重要的语句,v-on="$listeners",如果不加会导致从第二层递归开始直到最底层,都无法使用emit向父组件传递事件,因为每一级节点的父组件都是自身,需要传给子组件一个事件,子组件才能使用emit调用。
    那么还有其他方式解决以上问题吗

    这就不得不说说uni-appvue3了,顺便也说说uni-appvue在递归组件这方面的不同点。

    1. uni-app不支持$listeners,vue3也移除了$listeners,那如何解决呢?下面是不使用$listeners的做法,在v2、uni-app和v3中都适用,无非是v3使用了组合式api。注意看注释,有三个改动点
    <template>
      <div>
        
        <div 
          class="node-name" 
          @click.stop="nodeClick(node)"
        >
          {{node.name}}
        div>
        <div 
          class="children" 
          v-if="node.children && node.children.length"
        >
          
          <tree-node 
            v-for="item in node.children" 
            :key="item.id" 
            :node="item" 
            @node-click="nodeClick"
          >
          tree-node>
        div>
      div>
    template>
    
    <script>
      export default {
        name: 'tree-node',
        props: {
          node: Object
        },
        methods: {
          // 改动点3,通过参数接收数据
          nodeClick(node) {
            this.$emit('node-click', node)
          }
        }
      }
    script>
    
    <style scoped>
      .node-name {
        cursor: pointer;
      }
      .children {
        padding-left: 20px;
      }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    1. 在vue中使用递归组件,无论v2还是v3,都只需要设置一个name属性,即可直接调用,在uni-app中除了设置name属性,还需要使用import引入自身,并使用components注册组件。
    // 引入自身
    import treeNode from './tree-node.vue'
    export default {
      name: 'tree',
      components: { treeNode },
      props: {
        node: Object
      },
      methods: {
        
      },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    主定理(Master Theorem)推导和理解(2)
    【前端】JavaScript
    接口幂等性最佳实践--redis+注解
    python四六级英语在线考试系统django337
    Android App开发超实用实例 | ​Broadcast
    [ROS]在VS Code下编写代码,汇总问题及解决办法
    元强化学习 论文理解 MAESN
    Gradle 自动化构建开源工具
    在get对象属性时发现没有get对应的方法
    Delphi终极版速扩展VCL应用程序
  • 原文地址:https://blog.csdn.net/xinTianou123/article/details/134502510