background

Recently, I was working on a visual background platform and needed to implement a recursive component. I used the Template template to write a loopComponent.vue recursive component, but after writing it, I found that it could not meet the requirements. Look at the example below.

Template version recursive component

  • LoopComponent. Vue file

Please note the following class=”aaaaaaaaaaaa”

<template>
  <! -- Notice the aaaaaaaaAAAAAAA here -->
  <div class="aaaaaaaaaaaaa">
    <div v-for="(item, index) in list" :key="index" class="list-item">
      <div class="item-name">
        <span>{{ item.name }}</span>
      </div>
      <template v-if="item.children">
        <loop-component :list="item.children" class="children-item" />
      </template>
    </div>
  </div>
</template>
<script>
export default {
  name: "loopComponent".props: {
    list: Array}};</script>
Copy the code
  • The main vue file
<template>
    <div class="list-detail">
        <loop-component :list="list"></loop-component>
    </div>
</template>
<script>
    export default {
        list: [{
          name: "Guangdong".children: [{
              name: "Guangzhou".children: [{
                  name: "Tianhe District"
                },
                {
                  name: "Huangpu District"
                }]
            },{
              name: "Shenzhen".children: [{
                  name: "Futian District"
                },
                {
                  name: Nanshan District}}}}]]]</script>
Copy the code
  • The rendered page looks like this

This may not look like a problem from the rendered page, but the fact is that the HTML structure rendered using the Template template has some drawbacks. Remember the class=”aaaaaaaaaaaa” notation, now let’s see where it is.

  • F12 renders the HTML structure

As you can see from the screenshots,class="aaaaaaaaaaaa"Three times. The recursive component adds the outermost div recursively at each level, although it looks like an extra layer is fine,But with some use to the slot slot component, component or components at the same level in the father and son will be a connection between data, if use the template version of the recursive components, due to a layer of div, originally the rendered element level by the father and son or siblings hierarchy into ye sun hierarchy, which can lead to some unexpected hidden bugs (find it really killing me, Firsthand experience...)That’s not what we want. Some quick-witted friends might think to put the outer layer<div class="aaaaaaaaaaaaa">Delete, in which case Vue will give you a big hint: Cannot use V-for on stateful component root element because it renders multiple elements. Ejbateful element = ejbateful element = ejbateful element. In Vue2, there was only one root element in the template, and only one div was written, but with the V-for directive there were multiple root elements, so this was not allowed (except Vue3).

To solve this problem, we need to introduce our main character render function.

Render function

What is the render function? In fact, the official text Dan in Vue has said very detailed cn.vuejs.org/v2/guide/re… .

Vue template compilation

In most development scenarios, we use the template syntax provided by Vue to declaratively describe the binding relationship between state and DOM, and then use the template to generate the real DOM and render it in the browser. When vue gets our template, it will compile it into a rendering function, and finally execute the rendering function to get VNODE. This is the general process of vUE template compilation.

Since we can’t remove the outermost div from the Template, we’ll start with the render function.

Recursive component rendering function version

  • LoopComponent. Js file
export default {
  name: 'LoopComponent'.props: {
    list: {
      type: Array.default: () = > {
        return[]}}},methods: {
    loopH(h, list) {
      return h(
        'div',
        {
          attrs: {
            class: 'list-item'
          },
          props: {
            list: list
          }
        },
        [
          h('div', {
            attrs: {
              class: 'item-name'
            }
          }, [
            h('span', list.name)
          ]),
          (() = > {
              if(! list.children || ! list.children.length)return []
              return list.children.map((item) = > {
                return this.loopH(h, item)
            })
          })()
        ]
      )
    }
  },
  mounted(){},render(h) {
    return this.loopH(h, this.list[0])}}Copy the code

My render method is a bit special, wrapping an extra loopH() recursive method to return a vNode object.

  • The main. Vue web page
<template>
    <div class="list-detail">
        <loop-component :list="list"></loop-component>
    </div>
</template>
<script>
    export default {
        list: [{
          name: "Guangdong".children: [{
              name: "Guangzhou".children: [{
                  name: "Tianhe District"
                },
                {
                  name: "Huangpu District"
                }]
            },{
              name: "Shenzhen".children: [{
                  name: "Futian District"
                },
                {
                  name: Nanshan District}}}}]]]</script>
Copy the code

Render recursive component versus template recursive component in HTML structure comparison

  • The template version

  • Render version

From the above two structure HTML structure diagram, the render version has no additional elements, every element is what we want, so it is very nice, perfect.

conclusion

While using the render function can render HTML more in line with developers’ expectations and reduce HTML hierarchy, the code is not as straightforward to write as the template template (except, of course,….) , both have their own advantages and disadvantages, according to the actual scene to use, I feel that only in the template template can not achieve the need to use the render function to achieve.