Categories
JavaScript Vue

Introduction to Vue.js Render Functions

Spread the love

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to define and use Vue.js render functions.

Basics

We can define and use render functions to programmatically display elements in our Vue app.

An example that can benefit from using render functions are components where there’re lots of v-if cases.

For example, if we want to display headings of various sizes with one component, we can define the following component:

src/index.js :

Vue.component("variable-heading", {  
  template: "#variable-heading-template",  
  props: {  
    size: {  
      type: Number,  
      required: true  
    }  
  }  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <script type="text/x-template" id="variable-heading-template">  
      <h1 v-if="size === 1">  
        <slot></slot>  
      </h1>  
      <h2 v-else-if="size === 2">  
        <slot></slot>  
      </h2>  
      <h3 v-else-if="size === 3">  
        <slot></slot>  
      </h3>  
      <h4 v-else-if="size === 4">  
        <slot></slot>  
      </h4>  
      <h5 v-else-if="size === 5">  
        <slot></slot>  
      </h5>  
      <h6 v-else-if="size === 6">  
        <slot></slot>  
      </h6>  
    </script>  
    <div id="app">  
      <variable-heading :size="1">foo</variable-heading>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code above works by passing in the size value to the variable-heading component as a prop and then the v-if in component template will determine which element to display depending on the size prop’s value.

It’s very long and has duplicate slot elements.

We can use render functions to reduce the code above to:

src/index.js :

Vue.component("variable-heading", {  
  props: {  
    size: {  
      type: Number,  
      required: true  
    }  
  },  
  render(createElement) {  
    return createElement(`h${this.size}`, this.$slots.default);  
  }  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <variable-heading :size="1">foo</variable-heading>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code above works by us adding a render method to our variable-heading component, which returns a Virtual DOM node, or VNode for short, by calling createElement function with the tag name as the first argument, and the children HTML nodes as an array as the second argument.

A Virtual DOM Node tells Vue what to render on the screen, which will be turned into a real DOM node.

The this.$slot.default have the child HTML nodes passed in between the opening and closing tags of variable-heading .

Nodes, Trees, and the Virtual DOM

HTML code are rendering dynamically by rendering a dynamic tree of elements called the DOM tree. DOM standards for the Document Object Model, which abstracts the HTML structure into a tree.

A tree has nodes and some nodes like text and HTML nodes are displayed in our browsers’ screens.

Vue manipulates and renders the DOM tree for us so that we don’t have to do it ourselves.

For example, we can create a component with a render function as follows:

Vue.component("variable-heading", {  
  data() {  
    return { msg: "hi" };  
  },  
  render(createElement) {  
    return createElement("p", this.msg);  
  }  
});

createElement Arguments

The createElement takes a few arguments.

The first argument is the HTML tag name, component options or async function that resolves to one of the 2 kinds of items mentioned before. It’s required.

The second argument is an object that has data corresponding to the attributes that we want to add to the template. It’s an optional argument.

The third and last argument is a string or array of the child VNodes built using createElement . This is also optional.

The Data Object

The data object has many properties that can control the attributes, classes, event handlers, inline styles of the created VNode.

The following properties are available:

class

It’s an object which has the class names that we want to include in the class attribute of the HTML element we render.

For example, we can write the following:

class: {  
  foo: true,  
  bar: false  
}

Then the foo class is included in the class attribute and bar isn’t.

style

An object with the styles that we want to include. For example, we can write the following:

style: {  
  color: 'yello',  
  fontSize: '12px'  
},

Then the styles above will be included with the element that’s rendered.

attrs

attrs object has the attributes and values that we want to include in the rendred HTML element. For example, we can write:

attrs: {  
  id: 'foo'  
}

Then our rendered HTML element will have the ID foo .

props

The props object has the component’s props with the corresponding value. For instance, we can write:

props: {  
  foo: 'bar'  
}

Then we can pass in the foo prop with the value 'bar' .

domProps

domProps object have the DOM properties that we want to pass in. For example, we can write:

domProps: {  
  innerHTML: 'foo'  
}

Then our rendered HTML element has the text foo inside.

on

With the on object, we can set event handlers for our rendered HTML element. It’s used for listening to events that are emitted via $emit .

For example, we can write:

on: {  
  click: this.clickHandler  
}

to listen to Vue click events.

nativeOn

We can listen to native browser events with the nativeOn object. It haves the same object as on expect that we have native event handlers instead of Vue event handlers.

directives

We can pass in the directives array to pass in one or more directives with their values, arguments, modifiers, etc.

For example, we can write:

directives: [  
  {  
    name: 'custom-directive',  
    value: '2',  
    expression: '1 + 1',  
    arg: 'foo',  
    modifiers: {  
      bar: true  
    }  
  }  
],

Then we can apply the custom-directive directive with modifiers, arguments, values, and expressions into the VNode.

scopedSlots and slot

We can pass in slots into our directive by setting it to a function that returns an element.

For scoped slots, we can write:

scopedSlots: {  
  default: props => createElement('span', props.text)  
}

For slots, we can just set the value as the name of the slot.

Other special top-level properties

key takes the key, ref takes a string ref name, refInFor is a boolean that is true if the same ref name is applied to multiple element.

Full Example

We can set the properties above as in the example below:

src/index.js :

Vue.component("anchor-text-block", {  
  render(createElement) {  
    const [slot] = this.$slots.default;  
    const headingId = slot.text.toLowerCase().replace(/ /g, "-");return createElement("div", [  
      createElement(  
        "a",  
        {  
          attrs: {  
            name: headingId,  
            href: `#${headingId}`  
          }  
        },  
        this.$slots.default  
      ),  
      createElement(  
        "p",  
        {  
          style: {  
            color: "green",  
            fontSize: this.fontSize  
          }  
        },  
        this.paragraphText  
      )  
    ]);  
  },  
  props: {  
    fontSize: {  
      type: Number,  
      required: true  
    },  
    paragraphText: {  
      type: String,  
      required: true  
    }  
  }  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <anchor-text-block paragraph-text="foo" :font-size="12"  
        >link</anchor-text-block  
      >  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we created an anchor-text-block component that 2 props, paragraph-text and font-size as indicated in the props property that we have in the component’s code.

The kebab case in the template is automatically mapped to camelCase in the JavaScript code.

In the render method, we called createElement , which creates a div , and then in the second argument, we have an array with the a and p elements created. This is where we passed in both props.

We also set the attrs property to #link since we have link as the slot’s inner text since that’s what we added between the opening and closing tags.

In the p element, we set the fontSize style from the prop and set the color to green.

Conclusion

We can use render functions to create components that are more flexible than our usual components.

It can have everything that components usually have like event handlers, directives, styles, attributes, nested elements and components and more.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *