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:
- An HTML tag name (or a component options object).
- A data object that corresponds to the attributes to be added to the HTML template (event listeners, class attributes, etc.).
- 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:
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:
- Creating an issue at the Github repo.
- Tweeting at us at @fullstackio.
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.