vue递归组件与函数式组件

125 阅读3分钟
  • 递归,在项目中的应用还是很多的,比如导航菜单,一级菜单里面有二级菜单,二级菜单里面有三级菜单。类似于各种树状结构的功能,都会用到递归。vue中组件也可以被用来递归(反复调用组件自身)。
  • 函数式组件,也可以作为 Vue 性能优化的一种方式。比如一些静态页面。

先说递归: 如图:假如我们现在要做一个类似多级导航菜单的功能 。

image.png


vue组件递归实现过程

下面我们也要写一个多级导航菜单的功能。 假设 parent.vue 组件data中有这样一组数据,

list: [
    {
      key: "1", 
      title: "Option 1"
    },
    {
      key: "2",
      title: "Navigation 2",
      children: [
        {
          key: "2.1",
          title: "Navigation 2.1",
          children: [
            {
              key: "2.1.1",
              title: "Option 2.1.1",
            }
          ]
        }
      ]
    },
    {
      key: '3',
      title: 'Option 3'
    }
]

parent.vue组件的template中是这样写的:

<ul>
  <template v-for="item in list">
    <li v-if="!item.children">{{ item.title }}</li>
    <li v-else>
      <div>{{ item.title }}</div>
      <sub-menu :menu-info="item.children" />
    </li>
  </template>
</ul>

注:

  • 上面使用了 <sub-menu /> 组件。首先需要在 script 中 import 这个组件,再注册组件。
  • :menu-info,将需要递归的数据以属性传参的形式传递给需要递归的组件。

sub-menu.vue组件代码如下:

<template>
  <ul>
    <template v-for="item in menuInfo">
      <li v-if="!item.children">{{ item.title }}</li>
      <li v-else>
        <div>{{ item.title }}</div>
        <sub-menu :menu-info="item.children" />
      </li>
    </template>
  </ul>
</template>

<script>
export default {
  name: 'SubMenu',
  props: ['menuInfo']
};

注:

  • props接收父组件传递过来数据。
  • <sub-menu :menu-info="item.children" />递归当前组件。并传参。

注意:

  • 被递归的组件必须有 name 选项,然后使用 name 选项的值作为组件名来递归当前组件。
  • 递归当前组件的时候,必须接收需要被递归的数据。

最后效果如下:

image.png

单文件组件

上面的 SubMenu.vue 组件是通过单文件组件的方式写的,打印这个组件实例,会有 render,data,以及生命周期函数等等 完整的vue组件的属性。

函数式单文件组件

下面是函数式组件打印的组件实例。

image.png

没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。这种组件就是函数式组件。因为函数式组件只是函数,所以渲染开销也低很多。

在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:

<template functional> </template> 

下面我们用函数式组件改写上面的 SubMenu.vue 组件。

<template functional>
  <ul>
    <template v-for="item in props.menuInfo">
      <li v-if="!item.children">{{ item.title }}</li>
      <li v-else>
        <div>{{ item.title }}</div>
        <sub-menu :menu-info="item.children" />
      </li>
    </template>
  </ul>
</template>

<script>
export default {
  name: 'SubMenu',
  props: ['menuInfo']
};

因为没有 this, 所有必须使用 props.xx 的形式获取传递过来的参数。

总结 :

  • 递归组件

    • 父组件以props形式给被递归的组件传递参数
    • 被递归的组件,以 props 形式接收
    • 被递归的组件,还需要 name 选项
    • 被递归的组件,当需要递归的情况下,则用当前组件的 name 选项值,作为被递归的组件名,并以 prop 形式传递参数给递归组件。
  • 函数式单文件组件

    • template 赋上一个 functional 属性即可
    • 单文件组件里面不能包含 data,生命周期等钩子,也不能使用 this 等 上下文。
    • 因为没有 this ,所以获取 props里面的值,使用 props.xx 形式获取。
    • 函数式组件,也可以作为 Vue 性能优化的一种方式。比如一些静态页面。