Vue 中使用渲染函数 render 实现无限节点的树

前言

我们在 Vue 的官方文档其中一节 渲染函数 & JSX 中有这么一句话:『Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。』,在实际项目中还真的遇到过一些使用模板解决不了的问题,如要生成一棵无限的 Dom 树,或者生成无限层级的菜单等情况。

这些情况下,使用模板真的是束手无策,使用过 react 的同学可能就知道,使用 jsx 可以轻松使用递归实现。当然使用 Vue 也可以使用 jsx 来实现,我也尝试去安装 babel 插件,结果插件要求 node 的版本 >=4, <=9,装不成功,想想我对 h 函数 还是挺熟悉的,算了,直接使用 h 函数来解决吧。

废话不多数,先上在线 DemoGithub 上源码地址

代码分析

template代码如下:

<template>
  <article class="pageview">
    <header class="header fixed">
      <div class="container"><a
          class="back back_ico"
          href="javascript:void(0);"
          @click="goBack"
        ></a><span class="title">{{msg}}</span></div>
    </header>
    <section class="main">
      <div
        id="menu"
        class="clearfix menu"
      >
        <node-content :nodes="data" />
      </div>
    </section>
  </article>
</template>

这里面,主要是使用了 node-content 这个本页面使用 render(createElement) 创建的自定义组件。

JS 代码段如下:

export default {
  name: 'RenderTreeDemo',
  components: {
    NodeContent: {
      props: {
        nodes: {
          required: true
        }
      },
      render (h) {
        // const nodes = this.nodes
        function clickHandler (e) {
          // console.log(e)
          let el = e.target
          let nextSibling = el.nextElementSibling
          if (nextSibling) {
            if (el.classList.contains('open')) {
              el.classList.remove('open')
              el.classList.add('close')
              nextSibling.classList.add('hide')
            } else {
              el.classList.remove('close')
              el.classList.add('open')
              nextSibling.classList.remove('hide')
            }
          }
        }
        function renderNode (nodes) { // 关键代码,递归生成树
          if (nodes.length > 0) {
            return h('ul', {}, nodes.map((_) => {
              return h('li', { key: _.id }, [
                h('a', {
                  'class': {
                    name: true,
                    open: _.children.length > 0
                  },
                  attrs: {
                    href: 'javascript:void(0);'
                  },
                  on: {
                    click: clickHandler
                  }
                }, [_.name])
              ].concat(
                _.children.length > 0 ? renderNode(_.children) : []
              ))
            }))
          }
        }
        return this.nodes.length > 0 ? renderNode(this.nodes) : h()
      }
    }
  },
  data () {
    return {
      msg: '渲染函数 Tree Demo',
      data: [
        {
          id: 0,
          name: '节点1',
          children: [
            {
              id: 1,
              name: '节点1-1',
              children: []
            },
            {
              id: 2,
              name: '节点1-2',
              children: [
                {
                  id: 7,
                  name: '节点1-2-1',
                  children: [
                    {
                      id: 10,
                      name: '节点1-2-1-1',
                      children: []
                    },
                    {
                      id: 11,
                      name: '节点1-2-1-2',
                      children: []
                    }
                  ]
                },
                {
                  id: 8,
                  name: '节点1-2-2',
                  children: []
                }
              ]
            }
          ]
        },
        {
          id: 3,
          name: '节点2',
          children: [
            {
              id: 4,
              name: '节点2-1',
              children: []
            },
            {
              id: 5,
              name: '节点2-2',
              children: []
            }
          ]
        },
        {
          id: 6,
          name: '节点3',
          children: []
        }
      ]
    }
  },
  methods: {
    goBack: goBack
  }
}

主要的思路:既然模板实现不了递归生成树,我们要使用 js 实现主要有三种选择,要么render 函数中使用 jsx;要么直接使用 render(createElement) 渲染函数带进来的 createElement 函数,其实就是 h 函数来实现递归生成;还有最后一种,就把自已做成一个节点组件,然后递归调用自己渲染循环数据,请稳步《Vue 中递归调用节点组件自身实现无限节点的树》。

扩展

实际应用中,我们往往需要扩展许多功能,如:对树接点绑定一些 Dom 操作的交互效果,对树节点增删改查,拖放改动节点结构等等。在 Vue 中有两种思路,要么通过 js 直接操作 dom,要么通过操作数据驱动 Dom 节点的变化。一般要结合着来使用与进行。感兴趣的同学可以通过深度学习 Element UITree 组件

作者: 博主

Talk is cheap, show me the code!

发表评论

邮箱地址不会被公开。

Captcha Code