I remember picking up CakePHP and I loved it and how easy it was to start using it. These documents are well-structured, exhaustive, and user-friendly. Years later, this is exactly what I felt in Vue.js. However, compared to Cake, the Vue documentation still lacks one thing: an actual project tutorial.

Regardless of the framework’s documentation, this is not enough for everyone. Reading concepts is not always helpful in understanding the big picture or how to use them to actually make something. If you’re like me, learn better by making some usable components and referring to the documentation when coding to help you memorize the documentation and become proficient with Vue.

In this tutorial, we will build the star rating system component. We will look at several concepts in vue.js that we will use in our project and discuss why we use them.

This article delves into the how and why. It is designed to help you grasp some of the core concepts of vue.js and teach you how to make design decisions for future projects. If you want to know the whole thought process, read on. Otherwise, you can view the final code on CodeSandbox.

An introduction to

Vue.js, of course, is fine if you run it yourself as a simple script, but it’s a little different when you want to use single-file components. Of course, you don’t have to build components this way. You can use Vue.com Ponent to define the global component perfectly. The problem is that there are trade-offs, such as having to use string templates, no CSS support for scopes, and no build steps (and therefore, no preprocessors). However, we want to learn more about how to build a real component that can be used in a real project. For these reasons, we’ll go with the actual setup supported by Webpack.

To simplify operations and shorten configuration time, we will use the vue-CLI and webpack-simple vue.js templates.

First, you need to install vue-CLI globally. Start the terminal and type the following:

npm install -g vue-cli
Copy the code

You need to continue typing:

vue init webpack-simple path/to/my-project
Copy the code

You will be asked several questions along the way. Select everything except “Use sass”, which is the default. Vue-cli will then initialize the project and create a package.json file. Once done, you can navigate to the project’s directory, install dependencies, and run the project:

cd path/to/my-project
npm install
npm run dev
Copy the code

No accidents! Webpack will start to serve project 8080 on the port (if available, port 8080 is not occupied by other programs) and trigger it in the browser. If all goes well, you should see a welcome page like this.

Short pause – debug tool

Almost! To debug vue.js components correctly, you need the right tools. Continue to install vue.js DevTools browser extension (Firefox/Chrome/Safari).

Your first component

One of the best features is the vue.js single file component (SFC). They allow you to define the structure, style, and behavior of components in a single file without the common disadvantages of mixing HTML, CSS, and JavaScript.

The SFC ends with the. Vue extension and has the following structure:

<template> <! -- Your HTML goes here --> </template> <script> /* Your JS goes here */ </script> <style> /* Your CSS goes here */ </style>Copy the code

Let’s create our first component: create a Rating. Vue file/SRC/Components and copy/paste the code snippet above. Then, open/SRC /main.js and adjust the existing code:

import Vue from 'vue'
import Rating from './components/Rating'

new Vue({
  el: '#app',
  template: '<Rating/>',
  components: { Rating }
})
Copy the code

Finally, add some HTML for your Rating.vue:

<template>
  <ul>
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
  </ul>
</template>
Copy the code

Now look at the page in your browser and you should see the list! Vue.js appends your

component to the #app element index. HTML. If you examine the HTML, you should not see the symbol for the #app element: vue.js replaces it with the Rating component.

Side note: Have you noticed that you don’t even need to reload the page? That’s because Webpack’s vue-loader comes with hot reload. In contrast to real-time reloading or browser synchronization, hot reloading does not refresh the page every time a file changes. Instead, it monitors component changes and only refreshes them, leaving the state unchanged.

Now we’ve spent some time setting it up, but now it’s time for us to actually write meaningful code.

Template template

We’ll use vue-awesome, an SVG icon component of vue.js built with the Font awesome icon. This allows us to load only the ICONS we need. Proceed to install it using NPM (or Yarn) :

npm install vue-awesome
Copy the code

Then edit your component as follows:

<template> <div> <ul> <li><icon name="star"/></li> <li><icon name="star"/></li> <li><icon name="star"/></li> <li><icon name="star-o"/></li> <li><icon name="star-o"/></li> </ul> <span>3 of 5</span> </div> </template> <script> import 'vue-awesome/icons/star' import 'vue-awesome/icons/star-o' import Icon from 'vue-awesome/components/Icon' export default  { components: { Icon } } </script>Copy the code

Ok ok, let’s slow down and explain all this 😅

Vue.js uses native ES6 modules to handle dependencies and exported components. The first two lines in the

Icon is also a vue.js single-file component (SFC), just like the component we’re building. If you open the file, you’ll see that it has exactly the same structure as ours.

The Export Default exports the text of the object as the view model for the component. We registered the Icon component in the current Components, so we can use it locally.

Finally, we use HTML in

Side note: Did you notice a

wrapper added around the HTML? That’s because we also added a counter
at the root level, and the component template in vue.js accepts only one root element. If you do not follow this rule, you will receive a compilation error.

The style of CSS

If you’ve been working with CSS for a while, you know that one of the main issues is having to deal with its global nature. Nesting has long been considered a solution to this problem. We now know that it can quickly lead to particularity problems, making styles hard to override, or impossible to reuse, and an extension nightmare.

Methods like BEM were invented to circumvent this problem and keep specificity low through namespace classes. For a while, it was the ideal way to write clean and extensible CSS. Then, frameworks and libraries like vue.js or React came along, bringing with them scope styles.

React has styled components, vue.js has component-wide CSS. It allows you to write component-specific CSS without having to provide any specific magic tricks. You write regular CSS using “normal” class names, and Vue. Js determines processing scope by assigning data attributes to HTML elements and attaching them to compiled styles.

Let’s add some simple classes to the component:

<template>
  <div class="rating">
    <ul class="list">
      <li class="star active"><icon name="star"/></li>
      <li class="star active"><icon name="star"/></li>
      <li class="star active"><icon name="star"/></li>
      <li class="star"><icon name="star-o"/></li>
      <li class="star"><icon name="star-o"/></li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>
Copy the code

And add a style:

<style scoped>
  .rating {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    font-size: 14px;
    color: #a7a8a8;
  }
  .list {
    margin: 0 0 5px 0;
    padding: 0;
    list-style-type: none;
  }
  .list:hover .star {
    color: #f3d23e;
  }
  .star {
    display: inline-block;
    cursor: pointer;
    margin: 0 1px;
  }
  .star:hover ~ .star:not(.active) {
    color: inherit;
  }
  .active {
    color: #f3d23e;
  }
</style>
Copy the code

Do you see the scoped property? This is why vue.js is told how to determine style ranges so they don’t leak anywhere else. If you copy/paste HTML code in index.html correctly, you’ll notice that your styles don’t take effect: that’s because they’re scoped to components! 🎉

So how to preprocess?

Vue.js makes it easy to switch from plain CSS to your favorite preprocessor mode. You just need to use the Webpack Loader correctly and set simple properties on the

We can now write component-level styles using Sass, importing Sass just like importing variables, color definitions, and so on. If you prefer the indentation syntax (or “sass” notation), simply switch SCSS to sass with the lang attribute.

Behaviors – behaviors

Now that our component looks good, it’s time to make it work. Currently, we have a hard-coded template. Let’s set up some initial simulation state and adjust the template so that it reflects it:

<script>
  ...
  export default {
    components: { Icon },
    data() {
      return {
        stars: 3,
        maxStars: 5
      }
    }
  }
</script>
Copy the code

Template modification:

<template>
  <div class="rating">
    <ul class="list">
      <li v-for="star in maxStars" :class="{ 'active': star <= stars }" class="star">
        <icon :name="star <= stars ? 'star' : 'star-o'"/>
      </li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>
Copy the code

What we are doing here is using Vue Data to set the component state. Each property you define is bound to the property in data: if it changes, it is reflected in the view.

We are making a reusable component, so data needs to be a factory function rather than an object literal. In this way, we get a new object rather than a reference to an existing object that will be shared between multiple components.

Our data factory returns two attributes: stars for the current number of “active” stars, and maxStars for the total number of stars. From this, we adjust our template so that it reflects the state of the actual components. Vue.js comes with a set of directives that let you add presentation logic to templates without having to mix it with pure JavaScript. The V-for directive traverses any iterable (array, object text, map, etc.). It can also take a number as a range of x repetitions. This is what we do with V -for=”star in maxStars”, so we

  • have one for each star in the component.
  • You may have noticed that some attributes are prefixed with colons: This is shorthand for the V-bind directive, which dynamically binds attributes to expressions. We could have written it in its non-elliptical form: V-bind :class.

    When the asterisk is active, we need

  • Append to elementactiveClass. In our case, that means every<li>Indexes should all be less thanstarTo addactiveClass. We are in:classThe directive uses an expression that is present onlystarLess thanstarsAppendactive. We use the same condition, this time using the ternary operator to define the Icon to be used with the Icon component:starorstar-o.

    How the counter should be

    Now that our star list is bound to the actual data, we should do the same for the counter. The easiest way to do this is to use “beard syntax” for text interpolation:

    <span>{{ stars }} of {{ maxStars }}</span>
    Copy the code

    Pretty straightforward, isn’t it? Now in our example, this will solve the problem, but if we need a more complex JavaScript expression, it’s best to abstract it in evaluated properties.

    export default {
      ...
      computed: {
        counter() {
          return `${this.stars} of ${this.maxStars}`
        }
      }
    }
    Copy the code

    This is a bit of an overcorrection. We can use expressions in templates to maintain readability. However, when you have to deal with more complex logic, remember to evaluate properties.

    The other thing we need to do is provide a way to hide the counter if we don’t want it. The simplest way is to use the V-if instruction with a Boolean value.

    <span v-if="hasCounter">{{ stars }} of {{ maxStars }}</span>
    Copy the code

    Add the hasCounter property to data

    export default {
      ...
      data() {
        return {
          stars: 3,
          maxStars: 5,
          hasCounter: true
        }
      }
    }
    Copy the code

    interaction

    We’re almost there, but we still need to implement the most interesting part of the component: reactivity. We’ll use vue.js directives that handle events with V-ON, and methods, a vue.js property that appends all methods.

    <template>
      ...
      <li @click="rate(star)" ...>
      ...
    </template>
    Copy the code

    The methods of

    export default {
      ...
      methods: {
        rate(star) {
          // do stuff
        }
      }
    }
    Copy the code

    We added an @click attribute to it

  • , which is a shorthand v-on:click. This directive contains a call to the Rate method we defined in the Methods component property.
  • Wait a minute…… This looks very familiar with HTML’s onclick property. Isn’t using inline JavaScript in HTML an old-fashioned mistake? “

    This is true, but even if the syntax looks a lot like onclick, comparing the two is a mistake. When you build a vue.js component, you should not think of it as separate HTML/CSS/JS, but as one component in multiple languages. When the project is compiled in the browser, all the HTML and instructions are compiled to pure JavaScript. If you examine the rendered HTML in a browser, you don’t see any indication of instructions, nor do you see any onclick attributes. Vue.js compiles your component and creates the correct binding. This is why you can access the component context directly from the template: directives are bound to the viewmodel. In contrast to traditional projects with separate HTML, templates are part of components.

    Back to our rate method. We need to change stars to the current li index at click, so we pass the index from the @click directive. We can do the following:

    export default {
      ...
      methods: {
        rate(star) {
          this.stars = star
        }
      }
    }
    Copy the code

    View the page in your browser and try clicking the star: It works!

    If you open the Vue panel in the browser DevTools and select the component, you will see the data change when you click the star. This indicates that your stars property is bound: when you modify it, it dispatches its changes to the view. This concept is called data binding, and if you’ve ever worked with frameworks like backbone. js or Knockout, you should be familiar with it. The difference is that vue.js, like React, only executes in one direction: this is called one-way data binding. But this topic can be discussed in another article 😊

    At this point, we can call it done, but we can do more to improve the user experience.

    Now, we can’t actually give a zero rating, because clicking on a star set stars to its index, and Li’s index is always greater than zero. It is better to re-click the same star and have it toggle its current state rather than remain active.

    export default {
      ...
      methods: {
        rate(star) {
          this.stars = this.stars === star ? star - 1 : star
        }
      }
    }
    Copy the code

    Now, if the index of the clicked star is equal to the current value stars, we reduce its value. Otherwise, we assign star to it.

    If we want to be thorough, we should also add a layer of protection to ensure that stars are never assigned a meaningless value. We need to make sure that stars is never less than zero, never more than maxStars, and that this is an appropriate number.

    export default {
      ...
      methods: {
        rate(star) {
          if (typeof star === 'number' && star <= this.maxStars && star >= 0) {
            this.stars = this.stars === star ? star - 1 : star
          }
        }
      }
    }
    Copy the code

    Passing props property

    The component’s data is now hard-coded in the data property. If we want our component to actually be usable, we need to be able to pass custom data from examples. In vue.js, we can do this with props.

    export default {
      props: ['grade', 'maxStars', 'hasCounter'],
      data() {
        return {
          stars: this.grade
        }
      },
      ...
    }
    Copy the code

    And in the main. Js:

    new Vue({
      el: '#app',
      template: '<Rating :grade="3" :maxStars="5" :hasCounter="true"/>',
      components: { Rating }
    })
    Copy the code

    Here are three things to note:

    First, we use the V-bind shorthand to pass props from component instances: this is what vue.js calls dynamic syntax. You don’t need it when you want to pass string values, and using literal syntax (without the normal attribute of V-bind) will do the trick. But in our case, since we’re passing numbers and Booleans, it’s important that we do so.

    The props and data features are combined at compile time, so properties in the props are bound by tags in the view model (if defined). But for the same reason, we can’t use the same name as the props and data properties, or the properties in Data will be overridden by the properties in props.

    Finally, we defined a Grade props and passed it as the stars property of a data. The reason we did this, rather than using grade props directly, is that when we change the level, the star changes. In vue.js, props are passed from the parent component to the child component, not the other way around, so you don’t accidentally change the parent state. This would violate the one-way data flow principle and make things harder to debug. This is why you should not attempt to modify properties within child components. Instead, it’s a good idea to define a data that uses the initial value of prop as its own local property.

    Final modification

    We’ll use a nice feature in vue.js: Prop Validation

    Vue.js allows you to control data before passing it to components. There are four main operations you can perform: check the type, require to define prop, set default values, and perform custom validation.

    export default {
      props: {
        grade: {
          type: Number,
          required: true
        },
        maxStars: {
          type: Number,
          default: 5
        },
        hasCounter: {
          type: Boolean,
          default: true
        }
      },
      ...
    }
    Copy the code

    We use type checking to ensure that the correct type of data is passed to the component. This is especially useful if we forget to use dynamic syntax to pass non-string values. We also ensure that grade is passed in accordance with the component’s given requirements. For the other items, we define default values so that the component works even if no custom data is passed.

    Now we can instantiate the component by doing:

    <Rating :grade="3"/>
    Copy the code

    That’s it! You created your first vue.js component and explored many concepts, including generation and boilermo vuE-CLI, single-file components, data passing in components, Scope style, instructions, event handling, computational properties, Vue Methods, single-mode data flow, Props and Prop Validation. And this is just the superficial functionality of vue.js!

    This is a very intensive tutorial, so don’t worry if you don’t understand everything. Read again, pause between sections, explore and play with the code on CodeSandbox. If you have any questions or comments about this tutorial, please don’t hesitate to post them on Weibo @me!

    At the end

    If you are lucky enough to have read this, I believe it will help you.

    This article is translated, and add some of their own discussion, many places are not smooth, please forgive me.

    The source code of this article is rating-vue-component, if useful to you, I hope you give a star