Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 和递归组件 #162

Open
husky-dot opened this issue Dec 9, 2019 · 0 comments
Open

Vue 和递归组件 #162

husky-dot opened this issue Dec 9, 2019 · 0 comments

Comments

@husky-dot
Copy link
Owner

作者:Milos Protic
译者:前端小智
来源:vuejsdevelopers


简介

有人说递归很难理解,也有人不这么认为。递归函数简单的定义是:一个自调用函数,这意味着它将在执行的某个时刻调用自己。

从理论上讲,递归是一种需要两个属性的行为:

  • 结束点:停止递归的情况
  • 一组规则:负责将所有的操作减少到结束点

咱们无法决定哪一个更重要。如果没有结束点,递归将成为一个无限循环,但是如果一组规则就不能实现期望的行为,所以两者都存在才能使它正常工作。


说到后台学习

clipboard.png


递归和 Vue 组件

在 Vue 中,递归非常有用。当然,不仅仅在 Vue 中,咱们可以遵循上面的规则在任何框架中实现递归行为。因此,根据给定的定义,咱们可以说递归组件是调用自身的组件。

递归组件什么时候有用? 只要咱们需要使用相同的模板结构,但需要使用分层输入数据,就可以使用递归。 如果树状视图(用于显示文件夹结构),网站上的注释,嵌套菜单等组件等等。

接着,咱们建立一个场景来演示递归组件的用途。

场景

想象一下,咱像往常一样来上班,给自己冲杯咖啡,开始阅读咱最喜欢的博客。突然,咱们的老板来了,说需要实现一个新的页面,在这个页面上,显示所有的文件夹、子文件夹和文件,且文件结构数量不确定。可以显示10个、5个或100个文件夹。接着,咱们喝着咖啡,开始挠头思考如何解决这个问题。最终,咱们会想到使用递归遍历来实现。

解决这个问题的组件的最少数量是1,但在咱们的示例中,咱们会创建两个组件:

  • root 组件
  • folder 组件

当然,咱们首先搞点数据来用:

数据

如前所述,当咱们有分层组织的数据,其中子数据具有与其父数据相同的结构时,递归就派上用场了。

const root = {
  text: 'Root Folder',
  leaf: false,
  expanded: true,
  children: [{
    text: 'Sub Folder 1',
    leaf: false,
    expanded: false,
    children: [{
      text: 'Sub Sub Folder 1',
      leaf: false,
      expanded: false,
      children: [{
        text: 'SomeFile1.js',
        leaf: true
      }]
    }, {
      text: 'Sub Sub Folder 2',
      leaf: false,
      expanded: false,
      children: []
    }, {
      text: 'SomeFile.txt',
      leaf: true
    }]
  }]
}

有了上面的数据,咱们开始创建组件。

root 组件

这个组件是咱们文件夹树的起点。它会开始所有子元素的沉浸,但是如果需要,它也可以显示一些独立的信息,因为它不是递归本身的一部分。

root 组件将包含一个folder属性,咱们会把root数据对象绑定到该属性上。此属性将传递给子组件,子组件将递归地创建基于它的文件夹树结构。

Template

<template>
  <ul class="folders">
    <li>Folders</li>
    <folder v-bind:folder="folder"></folder>
  </ul>
</template>

代码

import Folder from './Folder.vue';

export default {
  name: 'root',
  props: {
    folder: Object
  },
  components: {
    Folder
  }
};

样式

ul.folders {
  padding: 1rem;
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  list-style: none
}
ul.folders > li:first-child {
  padding: 1rem 1rem 1rem 0
}

就是这么简单。

folder 组件

此组件负责渲染树中的每个文件夹。它负责显示关于当前文件夹的信息,并渲染其子文件夹(如果有的话)。此外,这些文件夹是可单击的,通过单击其中一个,组件将显示其子文件夹和文件。

Template

<template>
  <li class="folder" v-bind:class="[folder.leaf ? 'is-leaf' : 'is-folder']">
    <span v-on:click="expand()">{{ folder.text }}</span>

    <ul class="sub-folders" v-if="folder.children && folder.children.length > 0" v-show="folder.expanded">
      <folder v-for="child in folder.children" v-bind:folder="child"></folder>
    </ul>
    <div class="folder-empty" v-else v-show="!folder.leaf && folder.expanded">No Data</div>
  </li>
</template>

Code

export default {
  name: "folder",
  props: {
    folder: Object
  },
  methods: {
    expand() {
      if (this.folder.leaf) {
        return;
      }

      this.folder.expanded = !this.folder.expanded;
    }
  }
};

样式:

li.is-folder {
  padding: 1rem;
  border-left: 1px solid #d3d3d3;
  margin-bottom: 0.5rem
}
li.is-folder > span {
  padding: 0.5rem;
  border: 1px solid #d3d3d3;
  cursor: pointer;
  display:inline-block
}
li.is-leaf {
  padding: 0 0 0 1rem;
  color: #000;
}
ul.sub-folders {
  padding: 1rem 1rem 0 0;
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  list-style: none
}
div.folder-empty {
  padding: 1rem 1rem 0 1rem;
  color: #000;
  opacity: 0.5
}

使用示例

为了使用咱们刚刚创建的组件,所需要做的就是将root组件导入到需要此功能的地方,并传递数据结构。例如,在 App.vue 中使用:

Template

<template>
  <div class="vue-app">
    <root v-bind:folder="root"></root>
  </div>
</template>

Code

import Root from './Root.vue';

export default {
  name: 'app',
  data: function () {
    return {
      root: {
        text: 'Root Folder',
        leaf: false,
        expanded: true,
        children: [{
          text: 'Sub Folder 1',
          leaf: false,
          expanded: false,
          children: [{
            text: 'Sub Sub Folder 1',
            leaf: false,
            expanded: false,
            children: [{
              text: 'SomeFile1.js',
              leaf: true
            }]
          }, {
            text: 'Sub Sub Folder 2',
            leaf: false,
            expanded: false,
            children: []
          }, {
            text: 'SomeFile.txt',
            leaf: true
          }]
         }]
        }
      }
    },
    components: {
      Root
    }
};

运行效果:

总结

递归并不像看起来那么难,它只是用不同的输入参数一次又一次地执行相同的代码块,直到达到结束点。希望本文能够更好帮大家理解递归以及如何使用Vue创建递归组件。


编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://devinduct.com/blogpost/32/vue-and-recursive-components


交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq449245884/xiaozhi

因为篇幅的限制,今天的分享只到这里。如果大家想了解更多的内容的话,可以去扫一扫每篇文章最下面的二维码,然后关注咱们的微信公众号,了解更多的资讯和有价值的内容。

clipboard.png

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant