Vue Patterns (Part — 2)

Vue Patterns (Part — 2)

Useful Vue patterns, techniques, tips, and tricks

In this article, I will continue 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 articles.

2. Component Communication

Props and Events

1. Vue components follow the one-way data flow

2. Props are a reactive data source

3. Child Components can only emit events to their direct parent (unlike the previous gif)

4. There is something called Event-Bus which is a global Vue instance that helps to share props between components without referring to parent-component

image.png

<template>
  <button @click="$emit('click');">{{ text }}</button>
</template>

<script>
export default {
  name: 'v-btn',
  props: {
    text: String,
  },
};
</script>
<template>
  <v-btn :text="buttonText" @click="handleClick"></v-btn>
</template>

<script>
export default {
  data() {
    return {
      clickCount: 0,
      buttonText: 'initial button text',
    };
  },
  methods: {
    handleClick() {
      this.buttonText = `Button clicked ${++this.clickCount}`;
      console.log('clicked', this.buttonText);
    },
  },
};
</script>

3. Component Event Handling

1. Listening to Events

We can use the v-on directive to listen to DOM events and run some JavaScript when they’re triggered

<div id="example-1">
  <button v-on:click="counter += 1">Add 1</button>
  <p>The button above has been clicked {{ counter }} times.</p>
</div>
.
.
.
var example1 = new Vue({
  el: '#example-1',
  data: {
    counter: 0
  }
})

2. Method Event Handlers

The logic for many event handlers will be more complex though, That’s why v-on can also accept the name of a method you’d like to call.

<div id="example-2">
  <!-- `greet` is the name of a method defined below -->
  <button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'Vue.js'
  },
  // define methods under the `methods` object
  methods: {
    greet: function (event) {
      // `this` inside methods points to the Vue instance
      alert('Hello ' + this.name + '!')
      // `event` is the native DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
})

// you can invoke methods in JavaScript too
example2.greet() // => 'Hello Vue.js!'

3. Method in Inline Handlers

Instead of binding directly to a method name, we can also use methods in an inline JavaScript statement

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})

4. Component Conditional Rendering

1. Directives (v-if, v-else and v-else-if)

Often in a web application, we want elements to appear on the page depending on if a condition is met or not.

In vue js, we have many options to perform conditional rendering for our elements, check the following snippet to see the different ways to perform that:

<h1 v-if="true">Render only if v-if condition is true</h1>
<h1 v-if="true">Render only if v-if condition is true</h1>
<h1 v-else>Render only if v-if condition is false</h1>
<div v-if="type === 'A'">Render only if `type` is equal to `A`</div>
<div v-else-if="type === 'B'">Render only if `type` is equal to `B`</div>
<div v-else-if="type === 'C'">Render only if `type` is equal to `C`</div>
<div v-else>Render if `type` is not `A` or `B` or `C`</div>

2. Directives (v-show)

v-show used to show or hide our element depending on if a condition is met or not

<h1 v-show="true">
  Always rendered, but it should be visible only if `v-show` conditions is true
</h1>

v-show VS. v-if

The main difference between the two is that:

1. v-if — Only renders the element to the DOM if the expression passes.

2. v-show — Renders all elements to the DOM and then uses the CSS display property to hide elements if the expression fails.

3. v-show — Does not support v-else, v-else-if

3. Render Function or JSX

If you use render functions or JSX in your vue application, you can apply all the following techniques:

1. if-else

2. switch-case

3. JSX (Object-Map)

4. Ternary operators

5. Logical operators

3.1 JSX (If-Else)

export default {
  data() {
    return {
      isTruthy: true,
    };
  },
  render(h) {
    if (this.isTruthy) {
      return <h1>Render value is true</h1>;
    } else {
      return <h1>Render value is false</h1>;
    }
  },
};

3.2 JSX (Switch-Case)

import Info from './Info';
import Warning from './Warning';
import Error from './Error';
import Success from './Success';

export default {
  data() {
    return {
      type: 'error',
    };
  },
  render(h) {
    switch (this.type) {
      case 'info':
        return <Info text={text} />;
      case 'warning':
        return <Warning text={text} />;
      case 'error':
        return <Error text={text} />;
      default:
        return <Success text={text} />;
    }
  },
};

3.3 JSX (Object-Map)

import Info from './Info';
import Warning from './Warning';
import Error from './Error';
import Success from './Success';

const COMPONENT_MAP = {
  info: Info,
  warning: Warning,
  error: Error,
  success: Success,
};

export default {
  data() {
    return {
      type: 'error',
    };
  },
  render(h) {
    const Comp = COMPONENT_MAP[this.type || 'success'];

    return <Comp />;
  },
};

3.4 JSX (Ternary-Operator)

export default {
  data() {
    return {
      isTruthy: true,
    };
  },
  render(h) {
    return (
      <div>
        {this.isTruthy ? (
          <h1>Render value is true</h1>
        ) : (
          <h1>Render value is false</h1>
        )}
      </div>
    );
  },
};

3.5 JSX (Logical-Operator)

export default {
  data() {
    return {
      isLoading: true,
    };
  },
  render(h) {
    return <div>{this.isLoading && <h1>Loading ...</h1>}</div>;
  },
};

5. Dynamic Component

Swappable Dynamic Components

Sometimes, it’s useful to dynamically switch between components, which is can be done using Vue’s <component> element with the special attribute :is

Component element just takes a string which is the component name or component definition using :is prop, After that Vue then looks up the component referenced by that string and renders it in place of the <component> tag

<template>
  <component :is="dynamicComponent"></component>
</template>

<script>
export default {
  props: {
    value: Boolean
  },

  computed: {
    dynamicComponent() {
      if(value) {
        return 'component-special';
      } else {
        return 'component-default';
      }
    }
  }
}
</script>

🔔 Notes

1. With the previous code example, the rendered component will be destroyed if a different component is rendered in <component>

2. If you want components to keep their instances without being destroyed, you should wrap the <component> tag in a <keep-alive> tag

<keep-alive> <component :is="currentTabComponent"></component> </keep-alive>

6. Functional Component

Stateless Functional Component

1. A functional component is a special-SFC, with no-script-tag

2. It only accepts props in order to display data.

3. To make your component a functional one, you should add a functional attribute to the template tag

fp-component.vue

<template functional>
  <h1>{{ props.title }}</h1>
  <p>{{ props.description }}</p>
</template>

index.vue

<template>
  <fp-component v-bind="{ title: 'FP Component', description: 'Only takes props' }" />
</template>

<script>
import FPComponent from './fp-component';

export default {
  components: {
    FPComponent
  }
}
</script>

Advantages

1. Faster rendering

2. Lighter memory usage

7. Renderless Component

Renderless Component

1. A renderless component is basically a component that does not render any HTML to the DOM

2. A renderless component makes use of the Slots API in order to achieve what we want.

     🔔 The only job of Renderless.vue is to provide the prop name

Renderless.vue

<script>
export default {
  render() {
    return this.$scopedSlots.default({ name: 'John' });
  }
};
</script>

App.vue

<template>
  <renderless v-slot="{ name }">
    <p>{{ name }}</p>
  </renderless>
</template>

<script>
import Renderless from './Renderless.vue';

export default {
  components: {
    Renderless,
  }
};
</script>

Advantages

1. Separate our logic from our markup