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 more advanced features of render functions.
A render function is a method of a component that gives us an alternative way to create HTML elements via the creation of Virtual DOM Nodes.
VNodes Must Be Unique
VNodes must be unique in the array that we pass into the second argument of the VNode function.
We can’t create VNodes that have duplicate child elements. For example, the following is invalid:
Vue.component("text-block", {
render(createElement) {
const dupNode = createElement("p", "hi");
return createElement("div", [dupNode, dupNode]);
}
});
…since we have 2 dupNode
in the array.
Instead, we should write:
Vue.component("text-block", {
render(createElement) {
return createElement(
"div",
Array.apply(null, { length: 5 }).map(() => {
return createElement("p", "hi");
})
);
}
});
to call createElement
multiple times to create fresh VNodes with the same content repeatedly.
Replacing Template Features with Plain JavaScript
We can replace v-if
with if
statements and v-for
with map
.
For example, we can create a person
component as follows:
src/index.js:
Vue.component("persons", {
props: ["persons"],
render(createElement) {
if (this.persons.length) {
return createElement(
"ul",
this.persons.map(p => {
return createElement("li", p);
})
);
} else {
return createElement("p", "Persons list is empty");
}
}
});
new Vue({
el: "#app",
data: {
persons: ["Joe", "Jane", "Mary"]
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="[https://cdn.jsdelivr.net/npm/vue/dist/vue.js](https://cdn.jsdelivr.net/npm/vue/dist/vue.js)"></script>
</head>
<body>
<div id="app">
<persons :persons="persons"></persons>
</div>
<script src="src/index.js"></script>
</body>
</html>
Then we get a list of persons
rendered.
This is the same as:
src/index.js
:
Vue.component("persons", {
props: ["persons"],
template: `
<ul v-if='persons.length'>
<li v-for='p of persons'>{{p}}</li>
</ul>
<p v-else>Persons list is empty</p>
`
});
new Vue({
el: "#app",
data: {
persons: ["Joe", "Jane", "Mary"]
}
});
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">
<persons :persons="persons"></persons>
</div>
<script src="src/index.js"></script>
</body>
</html>
v-model
There’s no equivalent to v-model
in render functions. We can implement equivalent logic as follows:
src/index.js
:
Vue.component("custom-input", {
props: ["value"],
render(createElement) {
return createElement("input", {
domProps: {
value: this.value
},
on: {
input: event => {
this.$emit("input", event.target.value);
}
}
});
}
});
new Vue({
el: "#app",
data: {
msg: ""
}
});
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">
<custom-input v-model="msg"></custom-input>
{{msg}}
</div>
<script src="src/index.js"></script>
</body>
</html>
In the code above, we emit the input
event in our custom-input
component and takes the value
prop. This is the same as v-model
.
Therefore, we can bind to a model of the parent with v-model
and when we type something into the input, the typed in data will show below the input.
Event & Key Modifiers
To add .passive
, .capture
and .once
event modifiers, we can prefix the events handlers with the following prefixes to add the modifiers to our event handlers:
.passive
— prefix with&
.capture
— prefix with!
.once
— prefix with~
.capture.once
or.once.capture
— prefix with~!
For example, we can write:
on: {
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
}
Other modifiers don’t need a prefix since they have equivalents in plain JavaScript. They’re the following:
.stop
— useevent.stopPropagation()
.prevent
— useevent.preventDefault()
.self
— useif (event.target !== event.currentTarget) return
- Keys — e.g.
.enter
, or.13
— useif (event.keyCode !== 13) { //... }
- Modifiers Keys — e.g.
.ctrl
,.alt
,.shift
,.meta
— useif (!event.ctrlKey) { //... }
For example, we can add a keyup
event handler as follows:
Vue.component("custom-input", {
props: ["value"],
render(createElement) {
return createElement("input", {
domProps: {
value: this.value
},
on: {
keyup: event => {
event.preventDefault();
this.$emit("input", event.target.value);
}
}
});
}
});
Then the input
event is emitted when a keyboard key is released.
We can use the modifiers that have special prefixes as follows:
src/index.js
:
Vue.component("custom-button", {
render(createElement) {
return createElement("button", {
domProps: {
innerHTML: "Click Me",
id: "child"
},
on: {
"!click": event => {
alert(`${event.target.id} clicked`);
}
}
});
}
});
new Vue({
el: "#app",
methods: {
onClick() {
alert(`parent clicked.`);
}
}
});
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">
<custom-button></custom-button>
</div>
<script src="src/index.js"></script>
</body>
</html>
Then we get child clicked
alert popup displayed since we used the capture
modifier on click.
Conclusion
If we want to write components with v-if
and v-for
but using render functions, we can replace them with if
statements and array’s map
method respectively.
For adding Vue event handlers, we can add modifiers to some of them with special prefixes and others by using plain JavaScript.
Also, we can add duplicate elements in an array of child VNodes.