Application of render function in Vue

Keywords: Javascript Attribute Vue

Preface

Because we have recently taken over maintenance of an ivew-based project, many custom functions are included in the new module, so render functions are used extensively; so we summarize them.. About render functions, the official documents also provide a more detailed description: render function: https://cn.vuejs.org/v2/guide... ; components are typically written using template templates; sometimes they cause redundancy in code and are not easy to extend.

Understanding render functions

Before learning the render function, it is better to understand the virtual node vNode and the virtual vDom composed of the virtual node tree to better understand the render function

/*
 *@describe {Rendering Function}
 *@params {See below} 
 *@returns {A VNode}
 */
createElement function: Usually written as h function, It's also officially recommended

h(
  // {String | Object | Function} => 'p' | 'Select' | (h, p) => {}
  // An HTML tag name, component option object, or
  // resolve has one of the async functions described above.Required.
  'div',

  // {Object}
  // A data object corresponding to an attribute in the template.Optional.
  {
     style: {
         width: '100px'
     },
  },

  // {String | Array}
  // A child virtual node (VNodes), usually an array: constructed from `h()', with parameters: ('tag|component', {attrs}, text)
  // A string can also be used to generate a Text Virtual Node.Optional.
  [
    'Write some text first',
   h('h1', 'A headline'),
   h(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

render function application 1:functional component

A functional component can be thought of as a function within the component, the input is a render context, and the return value is a rendered HTML string
For functional components, you can define as follows:

Stateless: The component itself is stateless
 Instanceless: The component itself has no instances, that is, no this  
Because there is no such in a functional component, parameters need to be passed by context.
export default {
  name: 'functional-button',
  functional: true,
  render (h, context) {
    return h('button','button 1')
  }
}

From the official documentation, the context parameter is as follows:

props: objects that provide all prop s
 Children: Array of VNode child nodes
 Slots: A function that returns an object containing all slots
 scopedSlots: (2.6.0+) An object that exposes an incoming scope slot.Normal slots are also exposed as functions.
Data: The entire data object passed to the component, passed into the component as the second parameter of the createElement
 Parent: reference to parent component
 listeners: (2.3.0+) An object containing event listeners registered by all parent components for the current component.This is an alias for data.on.
injections: (2.3.0+) If the inject option is used, the object contains attributes that should be injected.

Using functional components

<template>
  <div class="home">
    <func-button>
      hello button
    </func-button>
  </div>
</template>

<script>
import FuncButton from '../function-components/func-button'
export default {
  name: 'home',
  components: {
    FuncButton
  }
}
</script>

Now let's transform this func-button.js component. The hello button text node above is the childern property of func-button.js (array | String). We can let the parent component control the button content of the component, rewriting as follows:

// export default {
//   name: 'functional-button',
//   functional: true,
//   render (h, context) {
//     return h('button','button 1')
//   }
// }
export default {
  name: 'funtional-button',
  functional: true,
  render (h, { children }) {
    return h('button', children)
  }
}

How do I add events?

The code is as follows:

<template>
  <div class="home">
    <func-button @click="handleClick">
      hello button
    </func-button>
  </div>
</template>

<script>
import FuncButton from '../function-components/func-button'
export default {
  name: 'home',
  components: {
    FuncButton
  },
  methods: {
    handleClick () {
      alert('You hit me')
    }
  }
}
</script>
// func-button component
export default {
  functional: true,
  // Component properties are integrated into data, so for simplicity we can use data instead of props, listeners, etc.
  // Write as H ('button', data, ['hello',... Children])
  render (h, { props, listeners, children }) {
    return h(
      'button',
      {
        attrs: props,
        on: {
          click: listeners.click
        }
      },
      children
    )
  }
}

render function uses 2: JSX syntax

Usually, it is easy to use temlate because it is intuitive and concise, but often writing template s sometimes feels redundant. If you want to control the rendering logic, such as follow-up, judgment, etc., you can consider using JSX.
Reference for using JSX

<script>
export default {
  name: 'h-title',
  props: {
    id: {
      type: Number,
      default: 1
    }
  },
  render () {
    const hText = `<h${this.id}>${this.$slots.default[0].text}</h${this.id}>`
    return <div domPropsInnerHTML={hText}></div>
  }
}
</script>

render function application 3: customized components

// List.vue component
<template>
    <div>
        <template v-for="(item, index) in data">
            <li :key="index" v-if="!render">{{ item }}</li>
            <ListItem
                v-else
                :key="`a${index}`"
                :render="render"
                :item="item"
            ></ListItem>
        </template>
    </div>
</template>

<script>
import ListItem from "./list-item"

export default {
    components: {
        ListItem
    },
    props: {
    // Support for custom rendering
        render: {
            type: Function
        },
        data: {
            type: Array,
            default: () => []
        }
    }
};
</script>

// list-item.js
export default {
    props: {
        render: {
            type: Function
        },
        item: {
            type: String
        }

    },
    render(h) { // createElement  
        return this.render(h, this.item)
    }
}
// In parent component
<List :data="['Banana','Apple','A mandarin orange']" :render="render"></List>
// You can render the labels and content you want
render (h, data) { 
      return <span>{data}</span>
    }

Posted by Darrel on Sat, 28 Sep 2019 09:31:05 -0700