Categories
JavaScript Vue

Vue.js Render Functions — Advanced Features

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 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 — use event.stopPropagation()
  • .prevent — use event.preventDefault()
  • .self — use if (event.target !== event.currentTarget) return
  • Keys — e.g. .enter, or .13 — use if (event.keyCode !== 13) { //... }
  • Modifiers Keys — e.g. .ctrl, .alt, .shift, .meta — use if (!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.

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 *