Vue.js vs other frameworks: Creating a simple app.

Vue.js is a framework that combines many features from other popular frameworks into a unique mix and adds some of its own cool features. In this post I will show you how to create simple a review app with vue.js and compare the process with other frameworks, I have experience with.

First step will be to create a starting structure. With a lot of libraries and configurations currently available to javascript developers, I prefer to use some sort of project scaffolding that comes with framework to save some time. Vue has a CLI for that. Lets install the CLI it by running:

npm install —global vue-cli

Or

yarn global add vue-cli

Once installed we can create a project

vue init webpack tv_shows_reviews

It will ask us some questions. We need to be sure to answer yes to “Install vue-router?” question. The rest is pretty self explanatory.

Then, after following the instructions of the CLI we have a working project running at localhost.It has the following files and folders in its root directory:

README.md
build
config
index.html
node_modules
package-lock.json
package.json
src
static

The code for the starting application is located at src folder which has the following structure:

├── App.vue
├── assets
│   └── logo.png
├── components
│   ├── TVShow.vue
│   └── TVShowsList.vue
├── main.js
├── mock_data.js
└── router
    └── index.js

Now it’s time to work on our own functionality.

Let’s start off by adding the components that will represent the routes we are going to have. We first create some basic structure for them. We add them to the src/components directory. First component will be inside a file TVShowList.vue (this extension is required for view to apply the template and styles to the component and properly export it):

<template>
  <div class="tv_show_list">TV show list</div>
</template>

<script>
export default {
  name: 'TVShowList'
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

Vue adopts a philosophy that HTML, CSS, and JavaScript of a component should all be inside one file. This is similar to how JSX resides inside a render function in React.js, only styles in React are usually put into a separate file.

The second one will be identical for now, except for it will be in a file TVShow.vue, have a name TVShow and a class tv_show, later it will contain a TV show’s description and reviews.

  The next step will be adding the routes definitions. They will be located inside routes/index.js file that was created for us by Vue CLI. Lets edit the file to contain the following:

import Vue from 'vue'
import Router from 'vue-router'
import TVShow from '@/components/TVShow'
import TVShowsList from '@/components/TVShowsList'

Vue.use(Router)

export default new Router({
  routes: [
    { path: ‘/show/:id’, component: TVShow },
    { path: '/', component: TVShowsList }
  ]
})

What we have done here is we imported the components we’ve just created into a route definitions module, we have also imported the vue and vue-router modules required for routing to work. Then we’ve used a Vue.use method to install the router plugin. Then we’ve defined the routes. Our index route (“/“) will serve the TVShowsList component and the “/show/:id” route will serve the TVShow component. The :id symbol is to match a URL like /show/1, where 1 is the id of a TV show.

Then we need to edit our App.vue file to remove the logo, it should look something like this:

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'app'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

<router-view /> component will contain our routes views.

Next Lets add some real content to the landing page. We are going to use some mock data for now(later you can integrate data from a database like MongoDB or any other database you prefer to use). Lets create some a mock_data.js file with some fictional TV shows.

export default [
  {
    id: '1',
    name: 'Physical Worker',
    image: ‘vintage-1133810_640.jpg’,
    reviews: [
    …
    ]
  },
  {
    id: '2',
    name: 'Hard Face',
    image: ‘white-male-1847732_640.jpg’,
    reviews: [
    …
    ]
  },
  {
    id: '3',
    name: 'Changing Catalog',
    image: ‘white-male-1871447_640.jpg’,
    reviews: [
    …
    ]
  }
]

The next thing is we import this data into our component. Normally this will be received as json via a ajax call, or if you are using some state management solution like redux or vuex, by means of those libraries. But since we writing a simple demo app lets use import for now. We create a mock_data.json in the /src directory of our project. Lets also add a data property to our component. The content inside the <script> tag should now look like this.

import TVShows from '../mock_data.js'

export default {
  name: 'TVShowList',
  data: function () {
    return {
      TVShows // Using ES6 here, this line is equivalent to TVShows: TVShows in ES5
    }
  }
}

When you use a component syntax in Vue, you have to use function for data property, but you can still return an object from it, which is exactly what we are going to do.

The next thing will be working on the template for the component

<ul class="tv_show_list">
  <li class="show_item" v-for="show in TVShows" v-bind:key="show.id">
    <router-link v-bind:to=“`/show/${show.id}`">
      <img v-bind:src="`static/${show.image}`" />
    </router-link>
    {{ show.name }}
  </li>
</ul>

We use the v-for directive of Vue to iterate over the TVShows array that is contained in a data property of the TVShows component. It puts the next show item into a show variable and renders the <li> item for each element of the array. The show variable will be available to all children of <li>. The v-bind:key directive is telling Vue to use the data contained in a show variable for the key of the current HTML element. This is basically needed if you want the element to not be replaced during rerenders (If you are familiar with React, the v-bind:key directive serves the same goal as React’s key attribute, the goal of preserving elements during reconciliation).

The iteration overall looks similar to AngularJS, where its done with directives (ng-repeat), rather than array functions like in React’s JSX.

Inside every li element we have a <router-link> component which wraps the image and from the image it creates a link to a detail page for the corresponding TV show. We use a es6 template string in a v-bind:to attribute of router-link, which allows us to write cleaner code.

Finally we render a show’s image. We need to put the images to /static folder, because the webpack configured to serve static assets from this folder by Vue template. But if you are comfortable with webpack configuration you can change publicPath property in webpack config to the path you need.

We also need to add some styles to our component, by adding css rules inside <style> tag.

.tv_show_list {
   padding: 0;
   display: flex;
   flex-direction: column;
   align-items: center;
 }
 .show_item {
   display: flex;
   flex-direction: column;
 }
 .show_item img {
   height: 200px;
   width: 200px;
 }

Now, when we visit the landing page of our application, we will be able to see a list of the shows and by clicking on the show image go to a show page (which is currently the same for every show, but this is the next thing we are going to change).

Again we are going to import mock data here:

import TVShows from ‘../mock_data.js’

Next we add the following properties to the TVShow component:

created: function () {
  setTimeout(() => {
    this.show = TVShows.find(show => show.id === this.$route.params.id)
  })
},
data: function () {
  return {
    show: {}
  }
}

We don’t have to use `created` lifecycle hook here, but I will use it to show you how to handle delayed data in an application with a real backend. Vue like react has lifecycle hooks. I am going to use `created` lifecycle hook here. It is fired when a component is created and data observation is available, you can refer to the documentation() for the details. For the purpose of making ajax calls it corresponds to componentWillMount in React. Here I use setTimeout to mock out an ajax call. Then we find the show that we need. When we declared earlier that the route for the TVShow component is:

{ path: ‘/show/:id’, component: TVShow }

We made the id variable available inside component as this.$route.params.id ($route.params gets added to a component for referencing the route variables) so now we use it to get the show that we need.

And data property is needed to set the default value for `show` property, we use it in a template, so Vue will throw an error if it is not defined when it is used.

Then in a template we have:

<div class="tv_show">
  <div>{{ show.name }}</div>
  <router-link v-bind:to="`/`">
    Home
  </router-link>
  <img v-bind:src="`static/${show.image}`" />
  <h1>Reviews</h1>
  <ul class="show_reviews">
    <li class="show_review" v-for="review in show.reviews” :key=“review.id”>
      <span>{{ review.reviewer }}</span>
      <p>{{ review.text }}</p>
    </li>
  </ul>
</div>

Which shows us the show with its reveiws. We added a router-link component here so that a user can go back to the list of shows(homepage), when they are done checking out a particular show. Also, one thing to note in this code here is that we use :key instead of v-bind:key, it is a syntactic sugar provided by Vue for v-bind directives.

Finally we add styles:


.show_reviews {
  list-style: none;
  text-align: left;
}
.show_review {
  border: 1px solid #ddd;
  border-radius: 5px;
  margin-bottom: 5px;
  padding: 5px;
}

And our 2 route simple starter application made with Vue is ready. You can check the application’s code on Github: https://github.com/jsmegatools/creating-a-simple-app-with-vue.js

Posted in Vue

Leave a Reply

Your email address will not be published. Required fields are marked *