您的当前位置:首页正文

vue递归组件实战之简单树形控件实例代码

2020-11-27 来源:星星旅游

1、递归组件-简单树形控件预览及问题

 

在编写树形组件时遇到的问题:

  • 组件如何才能递归调用?
  • 递归组件点击事件如何传递?
  • 2、树形控件基本结构及样式

    <template>
     <ul class="vue-tree">
     <li class="tree-item">
     <div class="tree-content"><!--节点内容-->
     <div class="expand-arrow"></div><!--展开或收缩节点按钮-->
     <div class="tree-label">小学</div><!--节点文本内容-->
     </div>
     <ul class="sub-tree"><!--子节点-->
     <li class="tree-item expand">
     <div class="tree-content">
     <div class="expand-arrow"></div>
     <div class="tree-label">语文</div>
     </div>
     </li>
     <li class="tree-item">
     <div class="tree-content">
     <div class="expand-arrow"></div>
     <div class="tree-label">数学</div>
     </div>
     </li>
     </ul>
     </li>
     </ul>
    </template>
    
    <style lang="stylus">
    .vue-tree{
     list-style: none;
     padding: 0;
     margin: 0;
     .tree-item{
     cursor: pointer;
     transition: background-color .2s;
     .tree-content{
     position: relative;
     padding-left: 28px;
     &:hover{
     background-color: #f0f7ff;
     }
     }
     .expand-arrow{
     position: absolute;
     top: 0;
     left: 0;
     width: 28px;
     height: 28px;
     cursor: pointer;
     &::after{
     position: absolute;
     top: 50%;
     left: 50%;
     display: block;
     content: ' ';
     border-width: 5px;
     border-style: solid;
     border-color: transparent;
     border-left-color: #ccc;
     margin: -5px 0 0 -2.5px;
     transition: all .2s;
     }
     }
     &.expand{
     &>.tree-content{
     background-color: #f0f7ff;
     &>.expand-arrow{
     &::after{
     transform: rotate(90deg);
     margin: -2.5px 0 0 -5px;
     }
     }
     }
     }
     .tree-label{
     height: 28px;
     line-height: 28px;
     font-size: 14px;
     }
     .sub-tree{
     display: none;
     list-style: none;
     padding: 0 0 0 28px;
     margin: 0;
     }
     &.expand>.sub-tree{
     display: block;
     }
     &.no-child{
     &>.tree-content{
     &>.expand-arrow{
     display: none;
     }
     }
     }
     }
    }
    </style>

    3、组件目录及数据结构

    目录结构

    vue-tree

    VueTree.vue
    TreeItem.vue

    树形控件数据结构

    let treeData = [
     {
     text: "一级", // 显示的文字
     expand: false, // 默认是否展开
     children: [ // 子节点
     {
     text: "一级-1",
     expand: false,
     },
     {
     text: "一级-2",
     expand: false,
     children: [
     {
     text: "一级-2-1",
     expand: false,
     },
     {
     text: "一级-2-2",
     expand: false,
     }
     ]
     }
     ]
     }
    ];
    

    3.1、 TreeItem.vue 代码

    <template>
     <li class="tree-item" :class="{expand: isExpand, 'no-child': !treeItemData.children || treeItemData.children.length === 0}">
     <div class="tree-content" @click="_clickEvent">
     <div class="expand-arrow" @click.stop="expandTree()"></div>
     <div class="tree-label">{{treeItemData.text}}</div>
     </div>
     <ul class="sub-tree" v-if="treeItemData.children && treeItemData.children.length > 0">
     <!--TreeItem组件中调用TreeItem组件-->
     <TreeItem
     v-for="item in treeItemData.children"
     :tree-item-data="item"
     :key="uuid()"
     :tree-click-event="treeClickEvent"></TreeItem>
     </ul>
     </li>
    </template>
    
    <script>
     export default {
     name: "TreeItem",
     props: {
     treeItemData: {
     type: Object,
     default(){
     return {};
     }
     },
     // 节点点击事件
     treeClickEvent: {
     type: Function,
     default() {
     return function () {};
     }
     }
     },
     data(){
     return {
     // 节点是否展开
     isExpand: this.treeItemData.expand || false
     }
     },
     methods: {
     // 展开/收缩
     expandTree(flag){
     if(!this.treeItemData.children || this.treeItemData.children.length === 0){
     return;
     }
     if(typeof flag === 'undefined'){
     flag = !this.isExpand;
     }else{
    
     flag = !!flag;
     }
     this.isExpand = flag;
     },
     // 创建一个唯一id
     uuid(){
     let str = Math.random().toString(32);
     str = str.substr(2);
     return str;
     },
     // 节点点击事件
     _clickEvent(){
     // 如果有传递事件函数,则调用事件函数并传递当前节点数据及组件
     if(this.treeClickEvent && typeof this.treeClickEvent === 'function'){
     this.treeClickEvent(this.treeItemData, this);
     }
     }
     }
     }
    </script>

    3.1.1、解决 组件如何才能递归调用? 问题

    在组件模板内调用自身 必须明确定义组件的name属性 ,并且递归调用时组件名称就是name属性。如在 TreeItem.vue 组件中组件的name名称为'TreeItem',那么在template中调用时组件名称就必须是 <TreeItem> 。

    当然也可以全局注册组件,具体可以查看vue官方文档 递归组件

    3.1.2、解决 递归组件点击事件如何传递? 问题

    我这里的解决方案是使用 props 将事件函数传递进来,在点击节点的时候调用事件函数,并把相应的数据传递进去。

    之前也尝试过使用 $emit 的形式并把数据传递过去,由于是递归组件,这样一直 $emit ,到最外层时传递的数据就变了,比如传递是第3层节点的数据,到最后执行时数据就变成第1层节点的数据了

    4、 VueTree.vue 组件

    <template>
     <ul class="vue-tree">
     <TreeItem
     v-for="(item, index) in treeData"
     :key="index"
     :treeItemData="item"
     :tree-click-event="treeClickEvent"></TreeItem>
     </ul>
    </template>
    
    <script>
     import TreeItem from "./TreeItem";
     export default {
     name: "VueTreeMenu",
     components: {
     TreeItem
     },
     props: {
     // 树形控件数据
     treeData: {
     type: Array,
     default(){
     return [];
     }
     },
     // 节点点击事件
     treeClickEvent: {
     type: Function,
     default() {
     return function () {};
     }
     }
     }
     }
    </script>
    
    <style lang="stylus">
    .vue-tree{
     list-style: none;
     padding: 0;
     margin: 0;
     .tree-item{
     cursor: pointer;
     transition: background-color .2s;
     .tree-content{
     position: relative;
     padding-left: 28px;
     &:hover{
     background-color: #f0f7ff;
     }
     }
     .expand-arrow{
     position: absolute;
     top: 0;
     left: 0;
     width: 28px;
     height: 28px;
     cursor: pointer;
     &::after{
     position: absolute;
     top: 50%;
     left: 50%;
     display: block;
     content: ' ';
     border-width: 5px;
     border-style: solid;
     border-color: transparent;
     border-left-color: #ccc;
     margin: -5px 0 0 -2.5px;
     transition: all .2s;
     }
     }
     &.expand{
     &>.tree-content{
     background-color: #f0f7ff;
     &>.expand-arrow{
     &::after{
     transform: rotate(90deg);
     margin: -2.5px 0 0 -5px;
     }
     }
     }
     }
     .tree-label{
     height: 28px;
     line-height: 28px;
     font-size: 14px;
     }
     .sub-tree{
     display: none;
     list-style: none;
     padding: 0 0 0 28px;
     margin: 0;
     }
     &.expand>.sub-tree{
     display: block;
     }
     &.no-child{
     &>.tree-content{
     /*padding-left: 0;*/
     &>.expand-arrow{
     display: none;
     }
     }
     }
     }
    }
    </style>

    5、使用树形组件

    <template>
     <div class="app" id="app">
     <VueTree :tree-data="treeData2" :tree-click-event="treeClickEvent"></VueTree>
     </div>
    </template>
    
    <script>
    import VueTree from "./components/vue-tree/VueTree";
    
    export default {
     name: 'app',
     data(){
     return {
     treeData2: [
     {
     text: "一级", // 显示的文字
     expand: false, // 默认是否展开
     children: [
     {
     text: "二级-1",
     expand: false,
     },
     {
     text: "二级-2",
     expand: false,
     children: [
     {
     text: "三级-1",
     expand: false,
     },
     {
     text: "三级-2",
     expand: false,
     children: [
     {
     text: "四级-1",
     expand: false,
     }
     ]
     }
     ]
     }
     ]
     },
     {
     text: "一级-2",
     expand: false
     }
     ]
     }
     },
     methods: {
     treeClickEvent(item, treeItem){
     console.log(item);
     }
     },
     components: {
     VueTree
     }
    }
    </script>

    总结

    以上所述是小编给大家介绍的vue递归组件实战之简单树形控件实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
    如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!