Vue Patterns (Part — 1)

Vue Patterns (Part — 1)

Useful Vue patterns, techniques, tips, and tricks

In this article, I will be discussing some useful Vue patterns, techniques, tips, and tricks to define the responsibilities and behaviors of your components. The following patterns are intended to increase the maintainability, performance, and scalability of your application.

In this article, I will cover a few patterns to add to your toolbox and will complete the rest of the different patterns in the coming article.

Component Declaration

In this section we are going to discuss many declaration patterns for your vue component

1. Global Component (String Template)

Vue.component('my-btn', {
  template: `
    <button class="btn-primary" @click.prevent="handleClick">
      <slot></slot>(clicked - {{count}})
    </button>
  `,
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    handleClick() {
      this.count++;
      console.log('clicked', this.count);
    },
  },
});

📝 Notes on Global Components:

1. Global component will be defined using Vue.component followed by new Vue({ el: '#container' }) to target a container element in the body of every page.

2. Works very well for small to medium-sized projects, where JavaScript is only used to enhance certain views.

3. In more complex projects, however, or when your frontend is entirely driven by JavaScript, these disadvantages become apparent.

🤦‍♂️ Disadvantages of Global Component:

1. Global definition: force unique names for every component where you can't have 2 or more components with the same name even they are in different places of your project.

2. String templates: using global component will enforce you to use template string to write your markup inside, which lack syntax highlighting and require ugly slashes for multiline HTML

template: `
    <button class="btn-primary" @click.prevent="handleClick">
      <slot></slot>(clicked - {{count}})
    </button>
  `

3. No CSS support means that while HTML and JavaScript are modularized into components, CSS is conspicuously left out without being scoped to your component

4. No build step restricts us to HTML and ES5 JavaScript, rather than preprocessors like Pug (formerly Jade) and Babel

5. Globals Make It Harder To Refactor Code: Module bundlers like Webpack are very popular nowadays, Modules make it easier to organize our code into separate files. This makes our code not only DRY but easier to refactor but using global components raises new problems

🤦‍♂️ 1st problem

What if the file requires a global variable, how do you know what it is? Is it a function / object / class ?

🙆‍♂️ 2nd problem

Using Vue.component is the same thing as using a global variable, and comes with the same problems, it may have been OVERWRITTEN because Javascript lets you do that to globals.

image.png

Here is a very useful article about Why You Shouldn't Use Vue.component

2.1 Global Component (createElement)

We can implement a Vue render function in any Vue component. Also, given Vue reactivity, the render function will be called again whenever a reactive property of the component gets updated.

Here's a quick example of how you could render an anchored-heading component that takes level as a prop and render a span that contains Hello World as a child directly from a component render function.

createElement(
  'anchored-heading', {
    props: {
      level: 1,
    },
  },
  [createElement('span', 'Hello'), ' world']
);

2.2 Global Component (Render Function)

Vue recommends using templates to build your HTML in the vast majority of cases. There are situations, however where you really need the full programmatic power of JavaScript.

That's where you can use the render function, a closer-to-the-compiler alternative to templates, That template doesn't feel great. It's not only verbose, but we're duplicating for every level and will have to do the same when we add a new level or child of the component

Vue.component('my-btn', {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    handleClick() {
      this.count++;
      console.log('clicked', this.count);
    },
  },
  render(h) {
    return h(
      'button',
      {
        attrs: {
          class: 'btn-primary',
        },
        on: {
          click: this.handleClick,
        },
      },
      this.$slots.default
    );
  },
});

3. Global Component (JSX)

If you're writing a lot of render functions, it might feel painful to write something like the previous one 😅

That's why there's a Babel plugin to use JSX with Vue, getting us back to a syntax that's closer to templates

Vue.component('my-btn', {
  data() {
    return {
      text: 'Click me',
    };
  },
  methods: {
    handleClick() {
      console.log('clicked');
    },
  },
  render() {
    return (
      <button class="btn-primary" @click.prevent="handleClick">
        {this.$slots.default}(clicked - {{count}})
      </button>
    );
  },
});

4. Single File Component (Most-Common)

Single file components are similar to regular components, but there are a few key differences that can make single file components the better tool for your project:

  • They can be defined locally, instead of globally.
  • You can define your component's <template> outside of your JavaScript, which allows for syntax highlighting in your text editor, unlike with string templates.
  • CSS/Styling information is included in the component definition.

Inspecting a Single File Component

Single file components are contained in files with the .vue extension. Each .vue file consists of three parts: template, script, style. Let's revisit our barebones component:

<template>
  <button class="btn-primary" @click.prevent="handleClick">
    <slot></slot>(clicked - {{ count }})
  </button>
</template>

<script>
import Vue from 'vue';
import Component from 'vue-class-component';


@Component
export default MyBtn extends Vue {
  count = 0;

  handleClick() {
    this.count++;
    console.log('clicked', this.count);
  }
}
</script>

<style scoped>
.btn-primary {
  background-color: blue;
}
</style>

💭 What About the Separation of Concerns?

For the first time, you may think that a SINGLE FILE COMPONENT violates the principle of separation of concerns but Let me elaborate a little on that point:

1. The separation of concerns is not equal to the separation of file types.

2. Instead of dividing the codebase into three huge layers, it makes much more sense to divide them into the component.

3. Collocating them actually makes the component more cohesive and maintainable.

4. You can still leverage its hot-reloading and pre-compilation features by separating your JavaScript and CSS into separate files

<template>
  <div>This will be pre-compiled</div>
</template>

<script src="./my-component.js"></script>
<script src="./my-component.css"></script>

5. React Class Component

Class syntax is one of the most common ways to define a React component. While more verbose than the functional syntax, it offers more control in the form of lifecycle hooks, so for me as a React lover, I wished that vue has a similar syntax for declaring a component.

// MyComponent.js
import React, { Component } from 'react';

class MyComponent extends Component {
  render() {
    return (
      <div>This is my component.</div>
    );
  }
}

export default MyComponent;

6. Vue Class Component

Thanks to the vue-class-component library We now got a class-based component in vue 😍🎉 by using the @Component prefix and extending your component from Vue, you now have a vue class-based component

Take a look at the following implementation for more details:

<template>
  <button class="btn-primary" @click.prevent="handleClick">
    <slot></slot>(clicked - {{ count }})
  </button>
</template>

<script>
import Vue from 'vue';
import Component from 'vue-class-component';

@Component
export default MyBtn extends Vue {
  count = 0;

  handleClick() {
    this.count++;
    console.log('clicked', this.count);
  }
}
</script>

<style scoped>
.btn-primary {
  background-color: blue;
}
</style>

Thank you for reading!