Vue.js - 通过渲染函数递归调用组件。 (仅限运行时)

时间:2017-02-10 23:28:34

标签: javascript recursion vue.js

我有一个很大的要求。我是一名高中生,我想和朋友一起为学生创建一个应用程序。在开始时我们想使用React作为我们的反应组件,但后来我们看到了Vue,它看起来非常好。但是由于事实,我们已经有很大一部分用twig编写的应用程序,我们并不想单独使用Vue.js,因为我们必须改变很多代码,特别是我的朋友,这是在Sympfony写的后端。所以我们使用仅运行时版本,它没有模板选项,所以我必须为我们的组件编写渲染函数。而且我遇到了一个特别的问题。

我正在编写文件管理器,我需要为每个文件夹渲染图层。代码比百万字更好,所以,请看一下:

var data = {
    name: 'My Tree',
    children: [
        {
            name: 'hello',
            isFolder: false,
        },
        {
            name: 'works',
            isFolder: true,
            children: [
              {
                  name: 'child2',
                  isFolder: true,
              },
              {
                  name: 'child3',
                  isFolder: false,
              },
            ]
        }

    ]
}


Vue.component('layer', {
  render: function renderChild (createElement) {

    if(data.children.length){
      return createElement('ul', data.children.map(function(child){
        return createElement('li', {
          'class' : {
            isFolder: child.isFolder,
            isFile: !child.isFolder
          },
          attrs: {
            id: "baa"
          },
          domProps: {
            innerHTML: child.name,
          },
          on:{
            click: function(){
              console.log("yes");
            },
            dblclick: function(){
              console.log("doubleclicked");
              if(child.children.length){
                  // if this has children array, create whole "layer" component again.

              }
            }
          }}
        )
      }))
    }
  },
  props: {
    level: {
      type: Number,
      required: true
    },
    name: {
      type: String,
    }
  }
})

new Vue({
  el: '#fileManagerContainer',
  data: data,

  render (h) {
    return (
      <layer level={1} name={"pseudo"}>
      </layer>
    )
  }
})

我的问题是,如何编写递归调用,如果元素包含子数组,则会在doubleclick事件上呈现整个Layer组件。

提前感谢您的任何反应,建议或答案:)

1 个答案:

答案 0 :(得分:0)

我知道这是一个非常老的问题,所以我的回答对OP不会有用,但是我想回答一下,因为我昨天遇到了同样的问题。

编写这些递归渲染函数的答案是完全不要尝试递归渲染函数本身。

对于我的示例,我有一组结构化文本(ish)-表示内容的对象数组-可以嵌套,就像这样:

[
  // each array item (object) maps to an html tag
  {
    tag: 'h3',
    classes: 'w-full md:w-4/5 lg:w-full xl:w-3/4 mx-auto font-black text-2xl lg:text-3xl',
    content: 'This is a paragraph of text'
  },
  {
    tag: 'img',
    classes: 'w-2/3 md:w-1/2 xl:w-2/5 block mx-auto mt-8',
    attrs: {
      src: `${process.env.GLOBAL_CSS_URI}imgsrc.svg`,
      alt: 'image'
    }
  },
  {
    tag: 'p',
    classes: 'mt-8 text-xl w-4/5 mx-auto',
    content: [
      {
        tag: 'strong',
        content: 'This is a nested <strong> tag'
      },
      {
        content: ' '
      },
      {
        tag: 'a',
        classes: 'underline ml-2',
        content: 'This is a link within a <p> tag',
        attrs: {
          href: '#'
        }
      },
      {
        content: '.'
      }
    ]
  }
]

请注意嵌套元素-这些元素需要递归才能正确呈现。

解决方案是将 actual 渲染工作移至以下方法:

export default {
  name: 'block',

  props: {
    block: {
      type: Object,
      required: true
    }
  },

  methods: {
    renderBlock (h, block) {
      // handle plain text without a tag
      if (!block.tag) return this._v(block.content || '')

      if (Array.isArray(block.content)) {
        return h(block.tag, { class: block.classes }, block.content.map(childBlock => this.renderBlock(h, childBlock)))
      }

      // return an html tag with classes attached and content inside
      return h(block.tag, { class: block.classes,  attrs: block.attrs, on: block.on }, block.content)
    }
  },

  render: function(h) {
    return this.renderBlock(h, this.block)
  }
}

因此render函数将调用renderBlock方法,而renderBlock方法将在需要时反复调用自身-递归发生在方法调用内。您将看到该方法可以检查content属性是否为Array类型-此时,它执行相同的渲染任务,但没有按原样传递内容,它将其作为Array映射传递,为数组中的每个项目调用相同的render方法。

这意味着,无论嵌套的内容有多深,它都将继续调用自身,直到到达栈的“底部”为止。

我希望这可以帮助将来节省一些时间-我当然希望我昨天能有这样的一个例子-我会为自己节省几个小时!