Vue3---手写Tree组件

首先我们分析数据的结构

treeOptions:[
   {
      lable:'一级',
      children:[
          {
             lable:'一级-1'
          },
          {
             lable:'一级-2'
          }
      ]
   },
   {
      lable:'二级',
      children:[
          {
             lable:'二级-1',
             children:[
                {
                 lable:'二级-1-1'
                },
                {
                 lable:'二级-1-2'
                }
             ]
          },
          {
             lable:'二级-2'
          }
      ]
   },
   {
      lable:'三级'
   }
  ]
// 数据的格式
// 如果是这种类型的,我们可以使用v-for进行渲染,写两层v-for。label就渲染出来了。 
treeOptions:[
   {
      lable:'一级',
      children:[
          {
             lable:'一级-1'
          },
          {
             lable:'一级-2'
          }
      ]
   },
   {
      lable:'二级',
      children:[
          {
             lable:'二级-1'
          },
          {
             lable:'二级-2'
          }
      ]
   },
   {
      lable:'三级',
      children:[
          {
             lable:'三级-1'
          },
          {
             lable:'三级-2'
          }
      ]
   }
]




// 但是这样的数据结构,没法知道具体用几个v-for,这个时候,我们可以考虑用递归的方式解决问题。
treeOptions:[
   {
      lable:'一级',
      children:[
          {
             lable:'一级-1'
          },
          {
             lable:'一级-2'
          }
      ]
   },
   {
      lable:'二级',
      children:[
          {
             lable:'二级-1',
             children:[
                {
                 lable:'二级-1-1'
                },
                {
                 lable:'二级-1-2'
                }
             ]
          },
          {
             lable:'二级-2'
          }
      ]
   },
   {
      lable:'三级'
   }
]

步骤:

  1. 定义Tree组件,并挂载到全局上,方便自己调用自己
    Vue3---手写Tree组件-LMLPHP

Vue3---手写Tree组件-LMLPHP

  1. 书写Tree组件
// Tree.vue
<template>
  <li v-for="(item,index) in Options" :key="index">
    {{ item.lable }}
    <!-- 当没有children的时候,就不用走递归了。-->
    <ul v-if="item.children&&item.children.length">
      <MyTree :options="item.children"></MyTree>
    </ul>
  </li>
</template>

<script setup>

import {ref} from "vue";

const prop = defineProps({
  options: {
    type: Array,
    default: () => {
      return []
    }
  }
})

//console.log(prop.options)
</script>

<style scoped>

</style>
// App.vue
<template>
  <h5>树型组件</h5>
  <Mtree :options="treeOptions" />
</template>

<script setup>
import {reactive} from "vue";

const state = reactive({
  treeOptions:[
   {
      lable:'一级',
      children:[
          {
             lable:'一级-1'
          },
          {
             lable:'一级-2'
          }
      ]
   },
   {
      lable:'二级',
      children:[
          {
             lable:'二级-1',
             children:[
                {
                 lable:'二级-1-1'
                },
                {
                 lable:'二级-1-2'
                }
             ]
          },
          {
             lable:'二级-2'
          }
      ]
   },
   {
      lable:'三级'
   }
   ]
})

const { treeOptions } = state
</script>

<style scoped>

</style>

  1. 添加点击后展开,再点击折叠的功能
=<template>
  <li v-for="(item,index) in myOptions" :key="index" @click.stop="openUl(item)">
    {{ item.lable }}
    <!-- 当没有children的时候,就不用走递归了。-->
    <ul v-if="item.children&&item.children.length" v-show="item.isOpen">
      <MyTree :options="item.children"></MyTree>
    </ul>
  </li>
</template>

<script setup>
/*
重点:通过isOpen属性的true,false,来使组件是否v-show隐藏显示

方法:
  1  添加myOptions ,使之双向绑定,绑定的数据来自prop.options传来的数据,不直接用prop.options传来数据的原因是因为要加isOpen,防止原数据遭到污染。
  const myOptions = ref(prop.options);

 2  在递归组件上添加v-show="item.isOpen"   用于显示与隐藏

 3 在根组件上添加 @click.stop="openUl(item)"
   	const openUl= (item)=>{
  		item.isOpen = !item.isOpen
	}

    加上.stop为了防止事件冒泡
* */
import {ref} from "vue";

const prop = defineProps({
  options: {
    type: Array,
    default: () => {
      return []
    }
  }
})

//console.log(prop.options)

/*
* 点击以后,展开ul,再点击,关闭ul
* */

const myOptions = ref(prop.options);
const openUl= (item)=>{
  item.isOpen = !item.isOpen
}
</script>

<style scoped>

</style>

关于使用v-for 和 递归时候,数据是怎么渲染出来的分析。

// v-for的形式
  treeOptions:[
   {
      lable:'一级',
      children:[
          {
             lable:'一级-1',
             //......下面有1万层children
          }
      ]
   },
   {
      lable:'二级',
      children:[
          {
             lable:'二级-1',
             //......下面有1万层children
          }
      ]
   }
   ]



// 使用v-for的写法:

<div v-for='(item,index) in Data'>
  <div v-for='(childItem,childIndex) in item.children'>
     ......  
  </div>
</div>

这个时候会先渲染 "一级" ,再渲染"一级-1""一级-1-1"......  "一级-1-1...-10000"

紧接着:"二级" ,再渲染"二级-1""二级-1-1"......  "二级-1-1...-10000"
  // 递归的形式
  treeOptions:[
   {
      lable:'一级',
      children:[
          {
             lable:'一级-1',
             //......下面有1万层children
          }
      ]
   },
   {
      lable:'二级',
   }
   ]


// 这个时候,如果使用v-for就不行了。为了渲染item1,需要写10000个v-for,但是item2只需要写一个v-for。

// 这个时候,递归发挥作用了,每次都是自己调用自己,传入的值总是children。当children没有的时候,再渲染item2,也就是"二级"
这个时候会先渲染 "一级" ,再渲染"一级-1""一级-1-1"......  "一级-1-1...-10000"
再渲染 "二级"
04-05 04:41