# My first Vue app

### Introduction

Tutorial paralysis is true beyond words. Whenever we learn a new technology or framework or language, the first step will be to purchase a course in Edtech platforms like [Udemy](https://www.udemy.com/), [Pluralsight](https://www.pluralsight.com/), and the like. Nothing wrong! There has to be some starting point.

From my experience, if we complete a course just watching, or even coding along does not take us very far. Taking notes helps, again not too much. What helps is to build on our own, even if it is something that has no business meaning, or is incomplete in terms of functionality, it still makes a lot of difference.

> Learning how to learn is an art that takes time to master.

I had to pick up Vue JS for one of the products I'm building. [Vue Mastery](https://www.vuemastery.com/) helps me learn in a very efficient way. After watching one of the [beginner videos](https://www.vuemastery.com/courses/real-world-vue-3-composition-api/building-a-vue-3-app-composition-api), I decided to build a similar app to what they built, along with capturing notes so that I can refer anytime in the future.

Publishing the same so that it helps someone like me who's venturing on to Vue.

### What I plan to build

In one of my [earlier posts](https://hellosambhavi.com/notes-from-a-course-that-helped-me-integrate-django-and-vue), I integrated Vue and Django. I built a one-pager Book Review app, where one can enter minimal details about a Book and the data is stored in django's default sqlite database, displaying the detail through a Vue component.

I plan to build the same Book Review App, but purely using Vue. I don't intend to store any detail in the database as my goal here is to start to get comfortable with Vue.

This app will simply display Book reviews fetched using axios from a [json server](https://my-json-server.typicode.com/), meant to create fake data for API testing.

### Come, let's code

On creating a Vue project, a good amount of code is auto-generated. I decided to reuse most of them, except for renaming `HomeComponent` to `ReviewList`, adding a new component called `ReviewCard`, modifying text in `AboutComponent`, displaying book reviews on the right side of the home page by fetching data from a fake json server using axios, checking in the code to Github, and finally deploying it to [Render](https://render.com/).

The [course](https://www.vuemastery.com/courses/real-world-vue-3-composition-api) has a slightly different content, removing entire contents from `HomeView` and performing dynamic routing to another page when a card is clicked. I don't plan to add code related to dynamic routing as part of this post. Probably that'll be part of another post.

Below is the high-level list of tasks that I learned ad performed

1. Create a Vue project
    
2. Single file Vue components
    
3. Fundamentals of Vue routers
    
4. Fetch external data using axios
    
5. Build and deploy the app
    

**Create a Vue project**

We create a Vue project using a command line tool `vue-create` which is powered by a build tool called [`Vite`.js](https://vitejs.dev/)

At a high level, `vue-create` creates the complete skeleton required for a Vue project. `Vite` is used for fast builds, also enabling hot module replacements.

We use `npx create-vue` command to create the project. Below are the settings I used.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689328924653/d9313eb9-34b3-437b-919b-6964a58dbef5.png align="center")

Then change the directory to the newly created folder book-review followed by `npm install.`

`npm-install` installs the required packages as present in `package.json`. On successful installation, we start the development server `npm run dev`

```javascript
cd book-review
npm install
npm run dev
```

If the server starts without any issues, we should see the following

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689329221915/b5560141-fb89-485f-bbfc-1ac59ffd85a6.png align="center")

Hitting the URL on the browser, we see the following page 😀

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689329328171/1cc671c2-be97-44ae-b768-264fa7c75386.png align="center")

The folder structure in my IDE looks like the below

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689329722442/4ceff4f2-576a-47c3-943c-985d90be27b0.png align="center")

`node-modules` - This folder contains the build dependencies. We usually do not check this in git

`public` - This folder contains files that are not required to be processed through `Vite`. When the project is created, it contains `favicon.ico` by default

`src` - This is the folder that contains the actual meat of the application. It contains the following folders:

* assets - to store images and stylesheets
    
* components - building blocks of the app. Think of it as a Lego block.
    
* routers - for navigation
    
* stores - for state management
    
* views - to store the different "views" of the application
    
* App.vue - this is the root component that in turn nests all the other components. Think of it as the final Lego model that gets built
    
* main.js - this is the one that renders the app and mounts it to the DOM
    

On top of these folders, there are a few other files, more for configuration purposes.

The key here is the `main.js` file that creates and mounts the app.

```javascript
import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')
```

`` `index.html` `` This file references the `#app` and is the starting point of our application. Below is the line in `index.html` that performs the same.

```javascript
<div id="app"></div>
```

**Single File Components**

A Vue app is made up of multiple Single file components (with extension `.vue`). Each component loads a part of the page. In the default Vue app that gets created, there are two components namely

* About Component
    
* Home Component
    

These are loaded when *About* and *Home* links are routed to. One of the important points that I made a note of about a Single file component is the structure of the same. It is as below

```javascript
<script setup>

</script>

<template>
    <p>Come, Review the books you read</p>
</template>

<style>
p {
   font-color: 'blue';
   text-align: 'center';
}
</style>
```

script - The javascript part goes here

template - The html part goes here

style - The css part goes here

### Let's make changes to the existing code

As I mentioned earlier, I renamed `HomeComponent` under `views` folder to `ReviewList` . Under `components` folder, I create a file and name it `ReviewCard`. The extension will be `.vue` This is the child component that is invoked by the parent `ReviewList` that in turn is invoked from `App.vue` which finally goes back to `index.html`

Let's make a few changes to the left side of `ReviewList` now.

In Vue, `props` is a way through which a parent component passes data to a child component. We use simple props in `HelloWorld` component to display the title.

```javascript
<h1 class="green">{{ msg }}</h1>
```

Here `msg` is the props. We pass corresponding data from the parent `App`

```javascript
<HelloWorld msg="Review the books you read!" />
```

I renamed the links on the left side to match the app that I'm building

```javascript
      <nav>
        <RouterLink :to="{ name: 'ReviewList' }">Book Reviews</RouterLink>
        <RouterLink :to="{ name: 'About' }">About</RouterLink>
      </nav>
```

On making these changes and executing `npm run dev`, the page looks like

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689490299955/0dabb2af-25e8-4b8e-98e0-199ca8d75f21.png align="center")

Let's now move on to the right side. The plan is to store a couple of book review information in a server, using props to pass the data from the parent `ReviewList` to the child `ReviewCard`

To store the data in a server, we use a fake json server that is commonly used for testing.

This is [the one](https://my-json-server.typicode.com/) that is suggested in the course.

To add data, I goto my github account, create a new public repository, create a file called db.json and add a bunch of data in it, and commit it back.

Here is [my Github link](https://github.com/SambhaviPD/fake-db-for-book-reviews) to the db.json file where I just added two review details.

Let's now add a `props` in `ReviewCard` like so

```javascript
<script setup>

defineProps({
    review: {
        type: Object,
        required: true,
    },
})
```

I then add the `template` part such that it displays the name of the book, the author, and the review of the book. For simplicity's sake, I took a review from [goodreads](https://www.goodreads.com/). Here is the snippet for `template`

```javascript
<template>
    <div class="review-card">
        <!-- Display Review data -->
        <h2>{{  review.title  }} by {{  review.author }}</h2>
        <br/>
        <p>Reviewed on {{ review.date }}</p>
        <br/>
        <p> <u>Here's my review: </u>{{ review.review }}</p>
    </div>
</template>
```

Since I come from a django background, the method of using such a template language in htmls to display values is easy to pick up.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">I'm referring to <code>{{ review.title }}</code> and associated tags</div>
</div>

### Time to add router information

By default, there is a folder called `router` and an `index.js` file in it. This holds the routes i.e. when the user clicks on a link which component should fire up is defined here. Below are the routes in our `index.js` file

```javascript
 routes: [
    {
      path: '/',
      name: 'ReviewList',
      component: ReviewList
    },
    {
      path: '/about',
      name: 'About',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('../views/AboutView.vue')
    }
  ]
```

What this means is on hitting the root i.e.`http://localhost:5173` We fire up the `ReviewList` component. The route for `About` looks slightly different as it is lazy loaded for performance reasons.

### Let's now add API-related code

The data is ready, the route is ready, and the next step would be to add the piece of code that invokes the API using axios. We install axios as the first step using `npm install axios`. We then add `services` folder and create a file `ReviewService.js`. This is where we write the invocation code from the fake json server that we created earlier.

```javascript
import axios from 'axios'

const apiClient = axios.create({
    baseURL: 'https://my-json-server.typicode.com/SambhaviPD/fake-db-for-book-reviews',
    withCredentials: false,
    headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
    }
})

export default{
    getReviews() {
        return apiClient.get('/reviews')
    },
}
```

Now that everything is done, we just add a piece of logic in our `ReviewList` that invokes this `ReviewService`, fetches the `reviews` which in turn is iterated to populate `ReviewCard`

On the script section, we use the OnMounted lifecycle hook where we call the service like so

```javascript
<script setup>
import { ref, onMounted } from 'vue'
import ReviewCard from '@/components/ReviewCard.vue'
import ReviewService from '@/services/ReviewService.js'

const reviews = ref(null)

onMounted(() => {
    ReviewService.getReviews()
    .then((response) => {
      reviews.value = response.data
    })
    .catch((error) => {
      console.log(error)
    })
})

</script>
```

The `template` section is super simple where we iterate the fetched objects and load the `ReviewCard` component

```javascript
<template>
  <div class="reviews">
    <ReviewCard v-for="review in reviews" :key="review.id" :review="review" />
  </div>
</template>
```

That's it, when we look at the browser we see

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689492493016/6d8b6ed7-8a93-4c8d-950e-5d9a8957a3be.png align="center")

Since I just added 2 reviews, they are displayed. Not that it is great on styling, but the purpose here is to see an end-to-end flow work. And it does! 🤩

### Time to deploy

Let's deploy it on a cloud hosting provider, the course suggests [Render](https://render.com/) and I go with it. It is extremely easy to use. Below are the steps that are required:

* In your local terminal build the code for production mode using `vite build`. On success, you should see a `dist` folder that contains the files that will be injected to `index.html` during runtime. My folder structure finally looks like
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689495891857/e5787e55-ad85-48a3-9c5e-372d30d77456.png align="center")
    
* Then sign up with [Render](https://render.com/)
    
* Click on New -&gt; Static Site
    
* Scroll down and add the [Github url](https://github.com/SambhaviPD/vue-book-reviews) where your code repo is present
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689492755826/79006a59-b194-42bd-9607-3bd90679c21d.png align="center")
    
    Render now knows what code is to be deployed. When we click on continue we see a page that is prepopulated corresponding to our code in Github. We just need to give the app a name and the build folder name (`dist` in our case).
    
* On submitting the form, the build starts to run and within a minute it is deployed and shows the final URL. In my case, I named the app `example-book-reviews` and below is how it looks like when I hit the browser
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1689493236366/c0e73e1f-147d-4275-ab7e-16e0635c4b01.png align="center")
    
      
    

### Conclusion

It was a great learning experience by building something from scratch, modify it to my needs, and more importantly capture my understanding in a blog like this.

Though it was just a one-pager, it still helped me reinforce the fundamentals of a Vue app, and how to deploy it in the cloud. I will now be able to apply this learning to [the product I'm building](https://hellosambhavi.com/series/building-wobu) and take that further.

Here's [my Github repo](https://github.com/SambhaviPD/vue-book-reviews) that has the complete code and [the json server](https://my-json-server.typicode.com/SambhaviPD/fake-db-for-book-reviews) with 2 book reviews that I added.

And [here's the app](https://example-book-reviews.onrender.com/) deployed on Render.

### References

[https://www.vuemastery.com/courses/real-world-vue-3-composition-api](https://www.vuemastery.com/courses/real-world-vue-3-composition-api)
