An overview of Vue.js

Vidya Ramakrishnan

Front-end developer at HasGeek

vidya-ram          vidya_ramki

 

Text

<!DOCTYPE html>
<html>
 <head>
  <title>Vue.js</title>
 </head>
 <body>
  <div id="app">
   <p>Hello everyone! Welcome to {{message}}</p>
  </div>

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

  <script>
   new Vue({
    el: '#app',
    data: {
      message: 'Girls who JavaScript - Meetup 6',
    }
   });
  </script>
    
 </body>
</html>

Vue.js is a progressive framework

It is easy to plug it into the part where you would like to have  a more richer user interaction

since it's core library is focused on the view layer

Directives

  • v-if, v-else, v-else-if: Conditional rendering
  • v-show:  Conditional rendering
  • v-for: For rendering list
  • v-bind: To bind attributes dynamically or to pass props
  • v-model: To create two way binding in forms
  • v-on: To attach an event listener

Methods, Computed properties and watchers

<template>
  <div class="main">
    <p class="text">Computed Properties</p>
    <textarea v-model="message" class="message"></textarea>
    <p>Message: "{{ message }}"</p>
    <p>String length: {{ messageLen }}</p>
  </div>
</template>

<script>
export default {
  name: 'ComputedProperties',
  data () {
    return {
      message: ''
    }
  },
  computed: {
    // messageLen is a computed property 
    messageLen () {
      if (this.message) {
        return this.message.length
      }
    }
  }
}
</script>

Vue CLI

A simple CLI for scaffolding vue.js spa projects

 

$ npm install -g vue-cli
$ vue init webpack my-project
$ cd my-project
$ npm install
$ npm run dev

 

       For a PWA template with webpack, run

$ vue init pwa my-project

Vue router

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

new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App },
});

main.js

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

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

<style>
  .fade-enter-active, .fade-leave-active {
    transition: opacity .5s
  }
  .fade-enter, .fade-leave-to {
    opacity: 0
  }
</style>

App.vue

Text

import Vue from 'vue';
import Router from 'vue-router';

import Home from '@/components/Home'
import Channel from '@/components/Channel'
import EditChannel from '@/components/EditChannel'

Vue.use(Router);

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
    },
    {
      path: '/:channel',
      name: 'Channel',
      component: Channel,
    },
    {
      path: '/:channel/edit',
      name: 'EditChannel',
      component: EditChannel,
    }
  ],
});

router.js

Text

import Vue from 'vue';
import Router from 'vue-router';

const Home = () => import('@/components/Home');
const Channel = () => import('@/components/Channel');

Vue.use(Router);

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
    },
    {
      path: '/:channel',
      name: 'Channel',
      component: Channel,
    }
  ],
});

router.js

Code splitting with async component and

webpack dynamic module loading

Single file component

Text

<template>
  <div>
      <p class="site-title">{{ message }}</p>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data() {
    return {
        message: "Welcome to HasGeek TV. Watch talks and discussions from past events here."
    };
  },
};
</script>

<style scoped>
.site-title {
  font-size: 25px;
  text-align: center;
}
</style>

Single file component

vue-loader

Template

Script (ES5/ES6)

Style

Text

vue-template-compiler

babel

pre-processors + post css

Render functions

ES5 JavaScript

CSS files

Text

Vue's parsing & compilation phases of single file component

with webpack plugins and loaders

webpack plugin

webpack loader

webpack loader

webpack loader

Text

Virtual Dom

"A lightweight JavaScript  data structure to represent what the DOM should look like at a given point of time" - Evan You

In simple terms, Virtual Dom is a JavaScript object that represents the Document Object Model(DOM)

Render Function

This function returns a virtual dom.

In Vue CLI, vue-template-compiler complies Vue templates into render function

Text

Lifecycle Hooks

The lifecycle hooks provide you a method with which you can trigger some functionality at different moments of a component's lifecycle.
 

beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy, and destroyed.

Text

Reactivity in Vue

Text

                        Image source: https://vuejs.org/v2/guide/reactivity.html

HasGeek TV app has many admin pages with forms. Our python flask backend has the capability to generate HTML forms and render them. General format that we follow when we require form for an application is as simple as defining the forms fields and backend takes care of providing the form HTML along with the required JavaScript.

First we tried creating a form component with it's template as the form html received from the backend. In case of Vue Cli, the templates are precompiled and cannot be changed later. Hence this was not an option

Then we tried using Vue's async component feature which lets you  load the component only when required but the issue is async components are cached after the first load which was an issue for us, since for edit forms the fields are prefilled which requires it to be fe

tched fresh for each page load. Finally Vue’s dynamic component and computed properties helped us reload the form component every time there was a change in it's template . This let us reuse the backend generated form (html and associated JS library) and saved us from writing the HTML all over again.

Use backend generated HTML forms

in the client side

<template>
  <div>
    <Form v-if="showForm"></Form>
  </div>
</template>
<script>
import axios from 'axios';
import Utils from '../assets/js/utils';

let vm = {};

export default {
  name: 'EditChannel',
  data() {
    return {
      showForm: false,
      errors: [],
    };
  },
  components: {
    Form: (resolve) => {
      resolve({
        template: vm.formTemplate,
      });
    },
  },

Async form component

created() {
    vm = this;
    axios.get(this.path)
    .then((response) => {
      this.formTemplate = 
        Utils.getVueFormTemplate(
            response.data.form);
      this.showForm = true;
    })
    .catch((e) => {
      vm.errors.push(e);
    });
  },
};
</script>
<template>
  <div>
    <component :is="Form"></component>
  </div>
</template>
<script>
import axios from 'axios';
import Utils from '../assets/js/utils';

let vm = {};

export default {
  name: 'AddChannel',
  data() {
    return {
      path: this.$route.path,
      formTemplate: '',
      formId: '',
    };
  },
  computed: {
    Form() {
      const template = this.formTemplate ? 
            this.formTemplate : 
            '<p>Loading..</p>';
      return {
        template,
        methods: {
          onFormSubmit() { 
             Utils.formSubmit(vm.formId); 
          },
        },
      };
    },
  },
  

Dynamic component &

Computed properties

 

 created() {
    vm = this;
    axios.get(this.path)
    .then((response) => {
      this.formTemplate = 
         Utils.getVueFormTemplate(
               response.data.form);
      this.formId = 
         Utils.getElementId(
               this.formTemplate);
    })
    .catch((error) => {
      this.error = error;
    });
  },
};
</script>
Made with Slides.com