“This is the 14th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

1. Introduction to render function

To put it simply, in Vue we use template HTML syntax to build the page, using the render function we can use JS language to build the DOM. Since vue is a virtual DOM, it also needs to be translated into a VNode function when it gets the template. Using the render function to build the DOM, Vue avoids the translation process.

When the render function is used to describe the virtual DOM, vue provides a function that is the tool needed to build the virtual DOM. It’s called createElement on its website. And the convention is called h.

1.1 virtual DOM

Vue tracks how it changes the real DOM by creating a virtual DOM. Take a closer look at this line of code:

return createElement('h1'.this.blogTitle)
Copy the code

What exactly does createElement return? It’s not an actual DOM element. It might be more accurately called createNodeDescription, because it contains information that tells the Vue what nodes need to be rendered on the page, including descriptions of their children. We describe such a node as a “virtual node”, often abbreviated to “VNode”. “Virtual DOM” is the name we use for the entire VNode tree built from the Vue component tree.

1.2 Parameter accepted by createElement
// @returns {VNode}
createElement(
  // {String | Object | Function}
  // An HTML tag name, component option object, or
  Resolve an async function of any of the above. Required fields.
  'div'.// {Object}
  // A data object corresponding to the attributes in the template. Optional.
  {
    // (see 1.3 for details)
  },

  // {String | Array}
  // Child virtual nodes (VNodes), built from 'createElement()',
  // You can also use strings to generate "text virtual nodes". Optional.
  [
    'Write some words first.',
    createElement('h1'.'A headline'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'}})])Copy the code
1.3 Use of render function
render:(h) = > {
  return h('div', {// Bind div to the class attribute
    class: {
      child: true.more: false},// bind styles to div
  style: {width:'200px'.height:'200px',},// bind the click event to div
    on: {
      click: () = > {
        console.log('Click event')}},})}Copy the code
1.4 Drill down into the Render function data object

Just as V-bind :class and V-bind :style are treated specially in template syntax, they also have corresponding top-level fields in VNode data objects. This object also allows you to bind normal HTML attributes, as well as DOM attributes such as innerHTML (which overrides v-HTML directives).

{
  // Same API as' v-bind:class ',
  // Takes a string, object, or array of strings and objects
  'class': {
    foo: true.bar: false
  },
  // Same API as' v-bind:style ',
  // Accepts a string, object, or array of objects
  style: {
    color: 'red'.fontSize: '14px'
  },
  // Plain HTML attribute
  attrs: {
    id: 'foo'
  },
  / / component prop
  props: {
    myProp: 'bar'
  },
  / / DOM attributes
  domProps: {
    innerHTML: 'baz'
  },
  // The event listener is in the 'on' property,
  // Modifiers such as' V-on :keyup.enter 'are no longer supported.
  // keyCode needs to be checked manually in the handler.
  on: {
    click: this.clickHandler
  },
  // Only for components, for listening on native events, not for internal component use
  // 'vm.$emit' triggers the event.
  nativeOn: {
    click: this.nativeClickHandler
  },
  // Custom instruction. Note that you cannot bind 'oldValue' in 'binding'
  // Assign, because Vue has automatically synchronized for you.
  directives: [{name: 'my-custom-directive'.value: '2'.expression: '1 + 1'.arg: 'foo'.modifiers: {
        bar: true}}].// The format of the scope slot is
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props= > createElement('span', props.text)
  },
  // If the component is a child of another component, specify a name for the slot
  slot: 'name-of-slot'.// Other special top-level attributes
  key: 'myKey'.ref: 'myRef'.// If you apply the same ref name to multiple elements in a render function,
  // then '$refs.myRef' becomes an array.
  refInFor: true
}
Copy the code
1.4 the constraint

All VNodes in the component tree must be unique.

This means that the following render functions are not valid:

render: function (createElement) {
  var myParagraphVNode = createElement('p'.'hi')
  return createElement('div'[// error - Duplicate VNode
    myParagraphVNode, myParagraphVNode
  ])
}
Copy the code

If you really need to repeat elements/components a lot, you can use factory functions to do it.

For example, the following render function renders 20 identical paragraphs in a perfectly legal way:

render: function (createElement) {
  return createElement('div'.Array.apply(null, { length: 20 }).map(function () {
      return createElement('p'.'hi')}}))Copy the code

2. Application of render function

2.1 Render a simple element
// app.vue (root component)

<template>
  <div id="app">
    <myRender></myRender>
  </div>
</template>

<script>
import myRender from './components/myRender'
export default {
  components:{
    myRender
  }
}
</script>
Copy the code
// myRender.vue

<script>
export default {
  render:(h) = > {
    return h('div', {class: {
        child: true.more: false
      },
      attrs: {
        id: 'foo'.name: 'child'},style: {width:'100%'.height:'200px',},domProps: {
        innerHTML: 'I'm a child of the Render'
      }
    })
  }
}
</script>

<style lang="stylus" scoped>
.child {
  background: pink
  font-size 24px
  letter-spacing 2px
}
.more {
  background: red
}
</style>
Copy the code

2.2 Adding subLabels
<script>
export default {
  render:(h) = > {
    return h('div',
      {
        class: 'wrapper'.attrs: {
          id: 'wrapper',},style: {width:'100%'.height:'250px'}, },[ h('h2'.'title'),
        h('div', {class: 'content'.attrs: {
            id: 'content',},style: {width:'800px'.height:'100px'
          },
          domProps: {innerHTML:'I am content'
          }
        })
      ]
    )
  }
}
</script>

<style lang="stylus" scoped>
.wrapper 
  background: pink
  letter-spacing 2px
  .content 
    margin 0 auto 
    background: red
    color #ffffff
    font-size 24px

</style>
Copy the code

2.3 Use JavaScript instead of templates

Vue’s rendering functions do not provide proprietary alternatives to what can easily be done in native JavaScript.

  1. In the v-if and V-for template syntax:
<ul v-if="items.length">
  <li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>

<script>
export default {
  data(){
    return{
      items: [1.2.3]}}}</script>
Copy the code

Render function implementation:

<script>
export default {
  render: function (createElement) {
    if (this.items.length) {
      return createElement('ul'.this.items.map(function (item) {
        return createElement('li', item.name)
      }))
    } else {
      return createElement('p'.'No items found.')}},data(){
    return{
      items: [1.2.3]
    }
  }
}
</script>
Copy the code
  1. v-model
<script>
export default {
  render:function(createElement) {
    var self = this
    return createElement('div',[
        createElement('div', {class: 'showContent'},self.inputValue),
        createElement('input', {class: 'content'.domProps: {value:self.inputValue
          },
          on: {input:function(event){
              self.inputValue = event.target.value
            }
          }
        })
      ]
    )
  },
  data(){
    return{
      inputValue:' '}},watch: {inputValue:function(){
      console.log(this.inputValue)
    }
  },
}
</script>

<style lang="stylus" scoped>
.showContent
  font-size 32px
  letter-spacing 2px
.content 
  margin 10px auto 
  color blue
  font-size 24px
</style>
Copy the code

2.4 Static Slot

The use of the enclosing $slots

  1. The parent component
<template>
  <div id="app">
    <myRender>
      <template v-slot:header>
        <div >The head</div>
      </template>
      <template #footer>
        <div >The foot</div>
      </template>
    </myRender>
  </div>
</template>

<script>
import myRender from './components/myRender'
export default {
  components:{
    myRender
  }
}
</script>
Copy the code
  1. Child components
<script>
export default {
  render:function(createElement) {
    let childHeader = this.$slots.header
    let childFooter = this.$slots.footer
    return createElement(
      'div',
      {
        class: 'showContent'.style: {width:'100%'
        }
      },
      [
        createElement('div', {class:'childHeader'},childHeader),
        createElement('div',childFooter),
      ]
    )
  },
}
</script>

<style lang="stylus" scoped>
.showContent
  letter-spacing 2px
  background-color red
  .childHeader 
    color blue
    font-size 24px
</style>
Copy the code

2.5 Scope slot

The use of the enclosing $scopedSlots

  1. The parent component
<template>
  <div id="app">
    <myRender :myLayout="layout">
      <template slot-scope="childMsg">
        <div >
          {{childMsg.text}}
        </div>
      </template>
    </myRender>
  </div>
</template>

<script>
import myRender from './components/myRender'
export default { 
  data(){
    return{
      layout: {header:'head'.footer:'feet'}}},components:{
    myRender
  }
}
</script>
Copy the code
  1. Child components
<script>
export default {
  render:function(createElement) {
    let self = this
    return createElement(
      'div',
      {
        style: {width:'100%'
        },
      },[
        self.$scopedSlots.default({
          text: this.myLayout.header
        })
      ]
    )
  },
  props: {myLayout:Object
  }
}
</script>
Copy the code

Reference Article

Reference Article