Components in Vue

Front-end development is a beautiful mess. I was lucky enough to get into web development in 2017, when frameworks like React, Vue, and Angular had become the primary way to deal with that mess. Web pages nowadays look and behave much differently as a result - the mess has been organized into neat little boxes called components!

"Separation of concerns" is a common thread in the history of front-end development. In the past, this was primarily achieved by separating HTML, CSS, and Javascript into different files. Components challenged that interpretation, instead keeping all code that pertains to a single visual element in one file - regardless of language. Separating code by entity helps developers think about problems holistically, instead of focusing on content, style, and behavior independently.

What is a component?

In Vue, a component is an instance of Vue with some configuration. In a "single-file component", that configuration is organized into the <template>, <script>, and <style> sections of a .vue file. Single-file components are used in many Vue projects, including those created by the vue-cli tool. I personally recommend this way of building components, as it embraces modularity while keeping content, style, and behavior somewhat separated.

The <template> holds the HTML of the component. These HTML elements accept Vue directives - powerful logical commands that hook the template to your component data. You can think of the HTML as the skeleton and the directives as the muscles of your component.

component.vue

<template>
  <div class="my-component">
    <h2 class="component-header">
      Header
    </h2>
    <p class="component-text">
      Count: {{ count }}
    </p>
    <button @click="increment">
    </button>
  </div>
</template>

The <script> contains the data and logic of the component. The core of the script is the data; computed properties, methods, watchers, and other parts of the scripts use this data to control the template. You can think of the script as the brain of your component.

component.vue

<script> 
  export default {
    data() {
      return {
        count: 0
      }
    },
    methods: {
      increment() {
        this.count += 1;
      }
    }
  }
</script>

The <style> section contains CSS, which is applied to the template. This is fairly straightforward, but some useful features like scoping and SCSS are integrated into style blocks. You can think of the style as the skin of your component.

component.vue

<style lang="scss">
  .my-component {
    text-align: center;

    .component-header {
    margin-bottom: 20px;

    .component-text {
      color: #660;
    }
  }
</style>

How do I write a component?

Let’s get a project going with vue-cli. Install it using npm.

Terminal

npm install -g @vue/cli-service-global

Create a project.

Terminal

vue create vue-components

Run the project.

Terminal

yarn serve

Now that the project is running, let’s build a component! Say we have a set of data, and we want to display that data in a table. We also want a text input that lets us enter a query and filter the data shown in the table. We also want a button that clears the text from the input field.

First, let’s create a file called SearchableTable.vue in the components directory. Inside that file, create <template>, <script>, and <style> sections.

SearchableTable.vue

<template>
<template>

<script>
</script>

<style>
</style>

Import this file into a view file in your project, add it to the components of the script, and to the template as <searchable-table/>.

Home.vue

<template>
  <div class="home">
    <searchable-table />
  </div>
</template>

<script>
  import SearchableTable from '@/components/SearchableTable.vue'

  export default {
    name: 'home',
    components: {
      SearchableTable
    }
  }
</script>

Our idea centers on a set of data, so let’s start with the script. Data used by the component can be defined in the data property of the script. Let’s add an array of objects with names, ages, and hair colors. We also want a search query, so add that to the data as well.

SearchableTable.vue

<script>
export default {
  name: 'SearchableTable',
  data() {
    return {
      dataset: [
        { name: 'Sam', age: 24, hair: 'blond'},
        { name: 'Bob', age: 37, hair: 'brown'},
        { name: 'Jeff', age: 12, hair: 'red'},
        { name: 'Mike', age: 72, hair: 'gray'},
        { name: 'John', age: 17, hair: 'red'},
      ],
      query: '',
    }
  }
}
</script>

If we want to generate some data based on other data, like a filtered list, we can use computed properties. Add one that filters the array by checking if the name, age, or hair color matches the query. Let’s also add a method that clears the query.

SearchableTable.vue

computed: {
  filteredDataset() {
    return this.dataset.filter((d) => {
      return d.name.includes(this.query)
        || `${d.age}`.includes(this.query)
        || d.hair.includes(this.query)
    })
  }
},
methods: {
  clearQuery() {
    this.query = '';
  }
}

Now that we have the data we need, let’s build out a simple template to get the component off the ground. Add a table, a text input, and a button. We can hook those elements into our data with directives like v-bind, v-if, and v-for. Add some basic style to the <style> section to get things laid out properly.

SearchableTable.vue

<template>
  <div class="searchable-table__wrapper">
    <input v-model="query" type="text">
    <button @click="clearQuery"></button>
    <table class="searchable-table">
      <tr v-for="item in filteredDataset">
        <td>{{item.name}}</td>
        <td>{{item.age}}</td> 
        <td>{{item.hair}}</td>
      </tr>
    </table>
  </div>
</template>

Bam! When we enter text into the input field, the query property of the script's data is updated. This triggers the computed property to update the list fed to the table, dynamically changing what is displayed! And when we click the button, the text input is cleared. The power of Vue!

Maybe we want to re-use the table part of this component, but not the search part. Let’s create a new file called MyTable.vue and add <template>, <script>, and <style> sections like before. Back in SearchableTable.vue, import MyTable.vue, add it to the components of the script, and replace the table in the template with <my-table :dataset=“filteredDataset” />.

SearchableTable.vue

<template>
  <div class="searchable-table__wrapper">
    <input v-model="query" type="text">
    <button @click="clearQuery"></button>
    <my-table :dataset="filteredDataset" />
  </div>
</template>
<script>
import MyTable from './MyTable.vue'

export default {
  name: 'SearchableTable',
  components: {
    MyTable
  },

In MyTable.vue’s script, add a props array and take in a prop called dataset. Add a table to the script, and hook the table to the dataset with a v-bind like before. Move the styles that we defined for the table from SearchableTable.vue to the style section in MyTable.vue.

MyTable.vue

<template>
  <table class="my-table">
    <tr v-for="item in dataset">
      <td>{{item.name}}</td>
      <td>{{item.age}}</td> 
      <td>{{item.hair}}</td>
    </tr>
  </table>
</template>

<script>
export default {
  name: 'MyTable',
  props: ['dataset']
}
</script>

Now we have a MyTable component that displays a set of data, and a SearchableTable that wraps MyTable with a search field and filter functionality. Even with this basic example, you can see the power of modular thinking!

Why should I use components?

The demands of modern front end development require tools that make it easy to break concepts into manageable pieces. Components just make sense. Keeping the HTML, Javascript, and CSS that pertains to a single visual element in one place is intuitive and easy to think about.

Modular design also allows for easier debugging and handling of issues. It can be difficult to track down a bug in a mess of interrelated parts, particularly front end bugs reported by end users. Tying the code to the visual elements themselves makes it easier to know where to look when someone reports that "something" breaks. Just look in "something".vue!

Components are also just fun to use. Building visual interfaces can be a headache, and turning it into a game of legos takes a lot of that headache away. I personally did not take to front end development until discovering modern frameworks like Vue that embrace the concept of the component. If you haven't given them a try, I definitely recommend you check them out!