Vue Components

In this lesson we will learn how to build our first component, which is one of the main building blocks for your Vue apps.

For now this component will live in the script section of our index.html file, but later we will learn how to structure our code in a much better way.

Let’s take the code from our previous lessons and change our friends list to use a custom component. First let’s clean up our view instance:

new Vue({
  el: '#app',
  data: {
    friends: [
      'Alice',
      'Bob',
      'Dave',
      'Monica'
    ]
  }
})

Now the only thing our Vue instance is concerned with is keeping a list of our friends and mounting itself on the appropriate DOM element.

Then we can move all our logic to a newly created custom component:

Vue.component('friend-list', {
  template: `<div>
    <ul>
      <li v-for="friend in friends">
        {{ greeting }} {{ friend }}!
      </li>
    </ul>
    <button v-on:click="bye">Say goodbye</button>
  </div>`,
  props: ['friends'],
  data: function () {
    return {
      greeting: 'Hello'
    }
  },
  methods: {
    bye () {
      this.greeting = 'Bye'
    }
  }
})

new Vue({
  el: '#app',
  data: {
    friends: [
      'Alice',
      'Bob',
      'Dave',
      'Monica'
    ]
  }
})

There is a lot going on here, so let’s take it apart a bit.

First note that the order in which we declare our component and instantiate Vue matters. If we declare the component after Vue is already instantiated, it won’t work!

Then let’s look at our component definition. Notice that we define a template inside the component where we moved our markup to:

template: `<div>
  <ul>
    <li v-for="friend in friends">
      {{ greeting }} {{ friend }}!
    </li>
  </ul>
  <button v-on:click="bye">Say goodbye</button>
</div>,`

This is what we call a string template. So far when we were working with our template directly in our HTML page we were dealing with a DOM template instead, which has several disadvantages compared to string templates, such as being unable to use camelCase props or component names in the HTML. You can still define them in Javascript in camelCase and can refer to them in HTML using kebab-case, but this restriction doesn’t apply in string templates. This is because DOM templates are loaded by the browser before our Vue instance is created, whereas string templates are handled by Vue.

Next notice this short line: props: ['friends']. This creates a prop called friends, which is just a property that we can pass to this component. We will use this prop to pass in our friends array. But first let’s change it a little bit:

props: {
  friends: {
    type: Array,
    required: true
  }
},

While we can use the shorthand form of declaring our props in an array like we did before, it is actually best practice to declare them like this instead. This also gives us access to a number of validation rules that we can use to make sure that our friends prop is present and of the type array. Otherwise our component would not work.

Finally, we declare our data and methods on this component. The method declaration is the same as before, but if you take a closer look at the data definition you will see an important difference:

data: function () {
  return {
    greeting: 'Hello'
  }
},

When we define data on our Vue instance we simply return an object, but now inside the component we have to define a function that returns the data object. That’s because if we were to render multiple components of the same type, they would all share the same data object. This prevents that, and Vue would throw an error if we tried to do it wrong.

Finally let’s actually render our component on the page by replacing our previous markup with the following and passing in our friends prop:

<friend-list :friends=friends></friend-list>

To recap, this is what our complete example should look like by now:

<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>LearnVue Beginner Course | Lesson 4</title>
  </head>

  <body>
    <div id="app">
      <friend-list :friends=friends></friend-list>
    </div>

    <script src="https://unpkg.com/vue@2.4.1/dist/vue.js"></script>

    <script>
      Vue.component('friend-list', {
        template: `<div>
          <ul>
            <li v-for="friend in friends">
              {{ greeting }} {{ friend }}!
            </li>
          </ul>
          <button v-on:click="bye">Say goodbye</button>
        </div>`,
        props: {
          friends: {
            type: Array,
            required: true
          }
        },
        data: function () {
          return {
            greeting: 'Hello'
          }
        },
        methods: {
          bye () {
            this.greeting = 'Bye'
          }
        }
      })

      new Vue({
        el: '#app',
        data: {
          friends: [
            'Alice',
            'Bob',
            'Dave',
            'Monica'
          ]
        }
      })
    </script>
  </body>
</html>

Let’s refresh our page and everything should still work as before.

Also not that this way will create a global component. In a future course we will learn the difference between globally and locally registered components. But for now you can continue on to learn about Computed Properties.

comments powered by Disqus