Categories
Vue 3

Vue 3 — Render Functions Events and Plugins

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create render functions and create plugins with Vue 3.

Event Modifiers Equivalents

The event modifiers have the following equivalents.

They are:

  • .stop — event.stopPropagation()
  • .prevent — event.preventDefault()
  • .self — if (event.target !== event.currentTarget) return
  • .enter or .13 — if (event.keyCode !== 13) return
  • .ctrl.alt.shift, or  .meta — if (!event.ctrlKey) return

For example, we can use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-input></custom-input>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("custom-input", {
        render() {
          return Vue.h("input", {
            onKeyUp: event => {
              if (event.target !== event.currentTarget) return;
              if (!event.shiftKey || event.keyCode !== 23) return;
              event.stopPropagation();
              event.preventDefault();
              //...
            }
          });
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We can call the plain JavaScript event methods and check their properties to add the modifier equivalents.

Slots

The this.$slots lets us add slots to our Vue apps.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-heading :level="1">foo</custom-heading>
    </div>
    <script>
      const app = Vue.createApp({});
      app.component("custom-heading", {
        render() {
          const { h } = Vue;
          return h(`h1`, {}, this.$slots.default());
        },
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

to add or custom h1 element with a slot inside.

this.$slots.default is the default slot.

JSX

We can also use JSX in our render functions if we install this Babel plugin.

This way, we can use the more convenient JSX syntax in our render functions.

For instance, we can write:

import AnchoredHeading from './AnchoredHeading.vue'

app.component("custom-heading", {
  render() {
    return (
      <AnchoredHeading level={1}>
        <span>Hello</span> world!
      </AnchoredHeading>
      );
    }
});

to write JSX instead of plain JavaScript in our render function.

Plugins

Plugins are a self-contained code that can be reused.

They add global-level functionality in Vue.

They can add anything, which includes global methods or properties.

It can also include global assets and mixins.

Instance methods can also be added with plugins.

We can add plugins by exporting an object with the install method:

export default {
  install: (app, options) => {
    //...
  }
}

We can then add our own global properties by writing:

export default {
  install: (app, options) => {
    app.config.globalProperties.$foo = (key) => {
      return 'foo';
    }
  }
}

We can also inject other plugins within a plugin.

For instance, we can write:

export default {
  install: (app, options) => {
    app.config.globalProperties.$foo = (key) => {
      return 'foo';
    }

    app.provide('i18n', options);
  }
}

We can add global mixins and directives right into our plugin code with the app.directive and app.mixin methods:

export default {
  install: (app, options) => {
    app.config.globalProperties.$foo = (key) => {
      return 'foo';
    }

    app.provide('i18n', options)

    app.directive('my-directive', {
      bind (el, binding, vnode, oldVnode) {
        // some logic ...
      }
      ...
    })

    app.mixin({
      created() {
        //...
      }
      //...
    })
  }
}

To use a plugin, we call the app.use method:

app.use(myPlugin, options)

Conclusion

There’re equivalents to directive event modifiers in render functions.

Render functions can also contain JSX if we add a plugin.

We can create plugins with various method calls.

Categories
Vue 3

Vue 3 — Teleport

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to use the teleport component to render elements and components in a different location in the DOM.

Teleport

We can use the teleport component to let us render parts of our Vue template in a location that’s different from its usual location in the DOM

This is handy for creating things like modals and overlays.

The DOM element that we want to render our items in must already exist.

Otherwise, we’ll get an error.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <teleport to="#footer">
        <p>footer</p>
      </teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.mount("#app");
    </script>
  </body>
</html>

We added the teleport component to our template with the to prop set to the selector to mount the content inside it.

Therefore, the div with ID footer will hold the p element that’s inside the teleport component.

Using with Vue components

If teleport has a Vue component, then it’ll remain a child component of the teleport ‘s parent.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
<teleport to="#modals">
  <div>A</div>
</teleport>
<teleport to="#modals">
  <div>B</div>
</teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.component("parent-component", {
        template: `
          <h2>parent</h2>
          <teleport to="#footer">
            <child-component name="james" />
          </teleport>
        `
      });

      app.component("child-component", {
        props: ["name"],
        template: `
          <div>{{ name }}</div>
        `
      });

      app.mount("#app");
    </script>
  </body>
</html>

We put the parent-component in the root template of our app.

It has a teleport that has to set to #footer .

So it’ll be rendered in the div with the ID footer .

It has the child-component inside it.

And that’ll be rendered inside div with ID footer also.

Using Multiple Teleports on the Same Target

We can have multiple teleports with the same selector set as their to prop value.

They’ll be rendered by in the order that they’re defined in.

For instance, if we have:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <teleport to="#footer">
        <div>foo</div>
      </teleport>
      <teleport to="#footer">
        <div>bar</div>
      </teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.mount("#app");
    </script>
  </body>
</html>

Then the rendered result would be:

<div id="footer">
  <div>foo</div>
  <div>bar</div>
</div>

Conclusion

The teleport component lets us render parts of a Vue template in a location that’s different from the usual location in the DOM.

Categories
Vue 3

Vue 3 — Render Functions Basics

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create render functions with Vue 3.

Render Functions

Templates should be used most of the time to build Vue components.

However, there may be cases where we need more flexibility.

For instance, we may want to add elements with dynamic tags.

Instead of writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <variable-heading :level="1">foo</variable-heading>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("variable-heading", {
        template: `
          <h1 v-if="level === 1">
            <slot></slot>
          </h1>
          <h2 v-else-if="level === 2">
            <slot></slot>
          </h2>
          <h3 v-else-if="level === 3">
            <slot></slot>
          </h3>
          <h4 v-else-if="level === 4">
            <slot></slot>
          </h4>
          <h5 v-else-if="level === 5">
            <slot></slot>
          </h5>
          <h6 v-else-if="level === 6">
            <slot></slot>
          </h6>
        `,
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We can make the heading tags dynamic with a render function.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <variable-heading :level="1">foo</variable-heading>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("variable-heading", {
        render() {
          const { h } = Vue;

          return h(`h${this.level}`, {}, this.$slots.default());
        },
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created the variable-heading component with a render method to render our HTML.

The h method lets us render our items.

The first argument is the tag name.

The 2nd is any props or attributes we want to pass to it.

And the 3rd argument is an array fo children.

this.$slots.default() is the default slot to let us populate content,

The rest of the component are the same as the other parts.

Every element in the DOM is a node, and the h method lets us render the nodes by nesting them as we do in a tree.

The Virtual DOM Tree

Vue keeps a virtual DOM tree to keep track of changes is needs to make to the real DOM.

The h method returns an object that has the information needed for Vue to update the read DOM.

h() Arguments

The h method takes 3 arguments.

The tag name is the first argument. It can be a string, object, function, or null .

If it’s an object, then it must be a component or async component.

The 2nd argument is an object with optional props.

The 3rd argument is a string, array, or object of child nodes.

It’s also optional.

VNodes Must Be Unique

We can’t have duplicate VNodes in our render function.

So we can’t have:

render() {
  const myParagraphVNode = Vue.h('p', 'hi')
  return Vue.h('div', [
    paragraph, paragraph
  ])
}

We can’t have 2 paragraph s in the array.

Conclusion

We can use render functions to tell Vue how to render components with JavaScript.

This is an alternative to templates.

Categories
Vue 3

Vue 3 — More Complex Render Functions

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create render functions with Vue 3.

Replacing Template Features with Render Functions

We can replace template features with render functions.

Directives like v-if and v-for can be replaced within render functions.

v-if can be replaced an if statement.

v-for can be replaced with an array of items.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <todo-list :todos="todos"></todo-list>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            todos: [{ name: "eat" }, { name: "drink" }, { name: "sleep" }]
          };
        }
      });

      app.component("todo-list", {
        props: ["todos"],
        render() {
          if (this.todos.length) {
            return Vue.h(
              "div",
              this.todos.map(todo => {
                return Vue.h("div", todo.name);
              })
            );
          } else {
            return Vue.h("div", "no todos.");
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created the todo-list component with the if statement to check the length fo the this.todos array.

And we called map to return an array of divs with the name .

v-on

The equivalent of the v-on directive is the on methods in the object in the 2nd argument.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-div></custom-div>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("custom-div", {
        render() {
          return Vue.h(
            "div",
            {
              onClick: $event => console.log("clicked", $event.target)
            },
            "click me"
          );
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created a custom-div component with a div.

The 2nd argument is an object that has the onClick method, which listens to clicks on the div.

The 3rd argument has the content between the div tags.

v-model

v-model ‘s equivalent is the modelValue prop and the onInput method.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-input v-model="value"></custom-input>
      <p>{{value}}</p>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            value: ""
          };
        }
      });

      app.component("custom-input", {
        props: ["modelValue"],
        render() {
          return Vue.h("input", {
            modelValue: this.modelValue,
            onInput: ev => this.$emit("update:modelValue", ev.target.value)
          });
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We have the modelValue prop with the custom-input component.

this.modelValue has the prop’s value.

We set that as the modelValue property’s value.

And then we emit the update:modelValue event to send the value to the parent.

Now when we type in something, it’ll be synchronized with the value state.

So we’ll see what we typed displayed.

Event Modifiers

Event modifiers can also be replaced with render functions.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-div></custom-div>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("custom-div", {
        render() {
          return Vue.h(
            "div",
            {
              onClick: {
                handler: ev => console.log(ev),
                capture: true
              },
              onKeyUp: {
                handler: ev => console.log(ev),
                once: true
              },
              onMouseOver: {
                handler: ev => console.log(ev),
                once: true,
                capture: true
              }
            },
            "click me"
          );
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

to add the event handler methods.

The options are the modifiers.

Conclusion

We can replace various directives with their equivalents with render functions.

Categories
Vue 3

Vue 3 — Transition Between Components and Lists

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at creating transition effects between components and lists.

Transitioning Between Components

We can transition between components with the transition and component components.

The component component is used for switching between components dynamically by setting their name.

For instance, we can use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .bounce-enter-active {
        animation: bounce-in 1.3s;
      }
      .bounce-leave-active {
        animation: bounce-in 1.3s reverse;
      }
      @keyframes bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.8);
        }
        100% {
          transform: scale(1);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="currentComponent = 'foo'">foo</button>
      <button @click="currentComponent = 'bar'">bar</button>
      <button @click="currentComponent = 'baz'">baz</button>
      <transition name="bounce" mode="out-in">
        <component :is="currentComponent"></component>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            currentComponent: "foo"
          };
        },
        components: {
          foo: {
            template: "<div>foo</div>"
          },
          bar: {
            template: "<div>bar</div>"
          },
          baz: {
            template: "<div>baz</div>"
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We created 3 components, foo , bar and baz .

And we to transition between them, we created 3 buttons to change the component names.

We also have the name prop and to set transition name.

And we also have the transition styles in the style tag.

When we click the buttons, we’ll see a transition effect before we see the content of the new component.

List Transitions

We can add transition effects to lists.

To do this, we can use the transition-group component.

Unlike transition , it renders the actual element.

A span is rendered by default.

We can change this with the tag attribute.

Transition modes aren’t available since we aren’t alternating between mutually exclusive elements.

Elements inside are always required to have a unique key attribute.

CSS transitions classes will be applied to inner elements and not to the container itself.

List Entering/Leaving Transitions

We can add a list enter or leave transitions with the transition-group component.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .list-enter-active,
      .list-leave-active {
        transition: all 1s ease;
      }
      .list-enter-from,
      .list-leave-to {
        opacity: 0;
        transform: translateY(35px);
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="add">Add</button>
      <button @click="remove">Remove</button>
      <transition-group name="list" tag="div">
        <p v-for="item in items" :key="item">
          {{ item }}
        </p>
      </transition-group>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            items: Array(10)
              .fill()
              .map(() => Math.random()),
            nextNum: 10
          };
        },
        methods: {
          randomIndex() {
            return Math.floor(Math.random() * this.items.length);
          },
          add() {
            this.items.splice(this.randomIndex(), 0, Math.random());
          },
          remove() {
            this.items.splice(this.randomIndex(), 1);
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We created a list from an array of random numbers.

And we added the add and remove buttons to let us add and remove items.

The transition effects are in the style tags.

The effects we added is the opacity change and vertical translation effects.

In the template, we have the transition-group component with the name prop set to list to make that the prefix of the transition class names.

tag is the tag we render for the container.

We added the key prop as required in the p element, which is rendered from the items list.

This way, Vue can tell where each item is and animate them correctly.

Finally, we have the add and remove methods to let us add and remove items from the items array.

randomIndex generates a random index to add or remove items.

Now when we click add or remove, we’ll see the transition effects applied.

Conclusion

We can transition between components and add transition effects to list items.