30-days-cover-image

30 Days of Vue

Render Functions and JSX

 

This post is part of the series 30 Days of Vue.

In this series, we're starting from the very basics and walk through everything you need to know to get started with Vue. If you've ever wanted to learn Vue, this is the place to start!

Render Functions and JSX

We took a look at the different types of component templates in yesterday's article. Today, we'll look to use a render function to create the markup of a component entirely with JavaScript.

From what we’ve seen in yesterday's article, it’s probably safe to say that creating the markup for Vue components (or instances) is a fairly straightforward process. We’ve seen some alternate template definitions like inline-templates and x-templates, but as a best practice, it’s best to stick with using an instance template option for simple components/instances.

We did mention that there’s another way to have the templates of our components be defined (hint: Single-File Components). We’ll be taking a small detour today before diving into SFC’s tomorrow!

Vue, at build time, takes the templates we create for our instances/components and compiles them to something known as render functions. It’s at these compiled render functions, where Vue builds a virtual representation of nodes that make up the virtual DOM.

As mentioned in an earlier article, Vue operates not directly on the browser’s Document Object Model (DOM) immediately, but on a virtual DOM. Vue uses the virtual DOM to maintain/manage and track the changes in our application in a “less-expensive” way (i.e. less expensive than immediately tracking the changes being made on the actual DOM).

Although Vue recommends for us to use templates to construct the markup of our instances in most cases, we’re also given the opportunity to directly use render functions to build the markup of our instances as well! By using render functions, we skip the compile step that Vue takes to compile our templates down.

We’ll spend some time in this article taking a look at these render functions and how we can use them to construct the markup of the same element we've built in the last article.

Render functions

In the last article, we discussed different template techniques to construct a simple card element that accepted a message prop.

Live version - https://30dofv-singlestringtemp.surge.sh

The markup of the card element we’ve created was pretty straightforward and contained a <div> element encompassing a <header> element. The text content of the <header> element displayed the value of the message prop.

<div class="render-card">
  <header class="card-header card-header-title">
    {{ message }}
  </header>
</div>

We’ll recreate the above markup step by step with the help of a render function that’s available as a property in every instance.

let renderComponent = {
  render() {
    // render function
  },
  props: ['message']
}

render functions in Vue always receive a createElement function as an argument.

let renderComponent = {
  render(createElement) {
    // render function
  },
  props: ['message']
}

The createElement function is able to create the “virtual” representation of the DOM nodes that Vue uses to track and subsequently render on the page. The createElement function takes three arguments of its own:

  1. An HTML tag name (or a component options object).
  2. A data object that corresponds to the attributes to be added to the HTML template (event listeners, class attributes, etc.).
  3. Child nodes of the parent node.

The HTML tag name for the parent node we want to construct is a div element. We'll return the createElement function and pass in a string of value 'div' as the first argument:

let renderComponent = {
  render(createElement) {
    return createElement('div');
  },
  props: ['message']
}

Since we’re interested in applying a card CSS class to the parent div element, we’ll declare the data object in the second argument of the createElement function to have an attrs property. In attrs, we'll specify a class key that has a string value of 'render-card':

let renderComponent = {
  render(createElement) {
    return createElement(
      'div', {
        'attrs': {
          class: 'render-card'
        },
      }
    );
  },
  props: ['message']
}

Though we won’t be doing much more, there are numerous different ways of defining attributes with the second argument data object. If you’re interested, be sure to check out the Vue documentation for a good summary.

To mimic the card we've built in the last article, the parent <div> element is to have a child <header> element of its own. In the third argument of the createElement function, we’re able to either specify a simple string to render text or an array to render more createElement functions (i.e. more elements). Since we’ll be rendering another generated element as the child, we’ll declare the createElement function within the child nodes array and give it a string value of 'header':

let renderComponent = {
  render(createElement) {
    return createElement(
      'div', {
        'attrs': {
          class: 'render-card'
        },
      }, [
        createElement('header')
      ]
    );
  },
  props: ['message']
}

The header child element is to have classes of its own so we’ll pass in an attributes object in the nested createElement function to declare the classes the header element should have:

let renderComponent = {
  render(createElement) {
    return createElement(
      'div', {
        'attrs': {
          class: 'render-card'
        },
      }, [
        createElement('header', {
          'attrs': {
            class: 'card-header card-header-title',
          }
        })
      ]
    );
  },
  props: ['message']
}

Thankfully, the child header element is to contain no child elements of its own and instead simply display the value of the message prop. To have the header element display the message prop as its child content we’ll declare this.message in the third argument of the nested createElement function. this.message will reference the message property available in the component as props:

let renderComponent = {
  render(createElement) {
    return createElement(
      'div', {
        'attrs': {
          class: 'render-card'
        },
      }, [
        createElement('header', {
          'attrs': {
            class: 'card-header card-header-title',
          },
        }, this.message)
      ]
    );
  },
  props: ['message']
}

And that’s it! Before we finish, it might be worth mentioning that oftentimes instead of writing the createElement function as is, the term createElement is often labelled as h (short for hyperscript which is a term often used in virtual DOM implementations). Shortening the createElement keyword to h would have our renderComponent now look like the following:

src/render-functions-example/main.js
let renderComponent = {
  render(h) {
    return h(
      'div', {
        'attrs': {
          class: 'render-card'
        },
      }, [
        h('header', {
          'attrs': {
            class: 'card-header card-header-title',
          },
        }, this.message)
      ]
    );
  },
  props: ['message']
}
 

This page is a preview of 30 Days of Vue

Get the rest of this chapter and 330+ pages of Vue instruction for free.

The entire source code for this tutorial series can be found in the GitHub repo, which includes all the styles and code samples.

If at any point you feel stuck, have further questions, feel free to reach out to us by:

Get Started Now Background Image

Get started now

Join us on our 30-day journey in Vue. Join thousands of other professional Vue developers and learn one of the most powerful web application development frameworks available today.

No spam ever. Easy to unsubscribe.