Joel's dev blog

Vue

October 19, 2018

10 min read

No time

Really, I’ve got no time to learn Vue. I need to get basic concepts in the shortest length of time.

Vue Cli 3.0

We are going to use Vue Cli@3.0.

Initial settings

Update nvm and npm to an appropriate version

j031:~/workspace $ nvm install 8.9
Downloading https://nodejs.org/dist/v8.9.4/node-v8.9.4-linux-x64.tar.xz...
######################################################################## 100.0%
Now using node v8.9.4 (npm v5.6.0)
j031:~/workspace $ npm --version
5.6.0
j031:~/workspace $ node --version
v8.9.4
j031:~/workspace $ 

then install vue cli

npm install -g @vue/cli

and check:

j031:~/workspace $ vue --version
3.0.5

Enable instant prototyping

Install:

npm install -g @vue/cli-service-global

And just load up your component file:

vue serve App.vue

And now if you are running your app on a container, it may show some error saying something like “invalid host error”. This is how you solve it:

Make vue.config.js in the project root:

// vue.config.js
module.exports = {
  devServer: {
    host: '0.0.0.0',
    port: 8080,
    disableHostCheck: true
  }
}

Then you are good to go. You will see your vue file loaded on the webpage. For more troubleshotting, see:

Go further

You can point to the entry file and Vue Cli will automatically bundle things up for you.

Create a project

vue create test

It will go over interactive prompts and select things as you would like them to be!

After then, it will initialize project files automatically. Beautiful! You don’t need to create a boilerplate for your Vue project right?

Props

Just like react you can put in props like:

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
// @ is an alias to /src

import HelloWorld from "@/components/HelloWorld.vue";

export default {
  name: "home",
  components: {
    HelloWorld
  }
};
</script>
  • See msg prop of HelloWorld component. You just specify them inline. Same as react, right?
  • You import the component inside the script tag.
  • You specify the imported component inside the components key of the option object.

Directives

Directives start with v-.

they apply special reactive behavior to the rendered DOM.

Example with v-if

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld v-if="seen" msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue";

export default {
  name: "home",
  data: function(){
    return { seen: true }
  },
  components: {
    HelloWorld
  }
};
</script>

v-if’s seen is like a state in react, I think. So If you set seen as true, HelloWorld component will be visible. However, if you write:

data: function(){
    return { seen: false }
  },

HelloWorld will not be visible.

So what does name key do? I suppose it’s used for the name when you import the component.

Another example with v-on

HiButton.vue

<template>
    <button v-on:click="sayHi"></button>
</template>
<script>
export default {
  name: "HiButton",
  methods: {
    sayHi: function() {
      alert("hi")
    }
  }
}
</script>
<style lang="sass" scoped>
    button
        background: #FB1134
        height: 100px
        width: 100px
</style>

Look at the code above. v-on:click is like onClick in react. And intuition tells us that you can put any various user interactions after on:, like submit. Same as react, you are passing a reference to the function sayHi to button. And in methods option, you specify the job you wanna do with sayHi.

The scoped styling works with div[attr] form. Vue makes the styling unique to the element, for example, by rendering the button with:

html

<button data-v-340c8b7a=""></button>

css

button[data-v-340c8b7a] {
    background: #FB1134;
    height: 100px;
    width: 100px;
}

And here’s Home.vue:

Home.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld v-if="seen" msg="Welcome to Your Vue.js App"/>
    <HiButton />
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue"
import HiButton from "@/components/HiButton.vue"
export default {
  name: "home",
  data: function() {
    return {
      seen: true
    }
  },
  components: {
    HelloWorld,
    HiButton
  }
}
</script>

How can I insert a custom prettier config

In the boilerplate created by Vue Cli, open eslintrc.js, and add "prettier/pretter" key to rules like this, if you were to disable semicolons in your javascript files:

module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: ["plugin:vue/essential", "@vue/prettier"],
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
    "prettier/prettier": [
      "warn",
      {
        semi: false
      }
    ]
  },
  parserOptions: {
    parser: "babel-eslint"
  }
}

Then, npm run lint to check. Done!

Components

In Vue, you can declare components in a few ways:

Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

This is just another way of writing:

<template>
    <li>{{ todo.text }}</li>
</template>
<script>
export default {
    name: 'todo-item',
    props: ['todo'] // props specified inside an array
}
</script>

And you already know that the object being exported inside the script tag is going to be spread over to the root Vue instance.

Data

Data are just like states in react. You give data property in the Vue instance.

One thing to remember is that data needs to be function inside template components.

Instance lifecycle hooks

Just like react. right? Just look at the API and find out what you need.

Lifecycle functions are also inside the vue instance, like:

let vm = new Vue({
    created: function(){
        console.log("I was just created")
    }
})

Note on arrow functions: do not use it

Arrow functions would be bound to the parent context (think about arrow functions used as member functions inside the class, making it needless to bind it to the class manually). So you should not use arrow functions because this will not be bound to the Vue instance but something else that a function finds as a parent.

Directive shorthands

v-on

<button @click="sayHi"></button>

v-bind

<a :href="url"> ... </a>

What is v-bind used for

It is used for manipulating attribute values inside html tags with data in Vue.

class

<div class="static"
     v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>

classes named active and text-danger will only render depending on bools isActive and hasError:

data: {
  isActive: true,
  hasError: false
}

or, if you want to make it even simpler:

<template>
<div class="static"
     :class="classObject">
</div>
</template>
<script>
export default {
    data: {
      classObject: {
        active: true,
        'text-danger': false
      }
    }
}
// or use a computed property
export default {
    data: {
        isActive: true,
        error: null
    },
    computed: {
      classObject: function () {
        return {
          active: this.isActive && !this.error,
          'text-danger': this.error && this.error.type === 'fatal'
        }
      }
    }
}
</script>

style

It’s pretty much the same.

<div v-bind:style="styleObject"></div>
// ...
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

For loops

<template>
  <div>
    <li v-for="(letter, index) in msgArr" v-bind:key="`letter-${index}`">
      {{ letter }}
    </li>
    </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  computed: {
    msgArr: function () {
      return this.msg.split(" ")
    }
  }
}
</script>

With the msg prop being “Welcome to Your Vue.js App”, this is going to show something like:

  • Welcome
  • to
  • Your
  • Vue.js
  • App

Event modifiers

You attach it with modifiers that start with a period. If you want to preventDefault(), you can simply:

<form v-on:submit.prevent="onSubmit"></form>

v-model

It’s for two-way data bindings.

Add element

It is supported for vue-cli@3.

vue add element

Importing

If you do not want to import components separately, just do:

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

...

in your index.js file.

i18n

Add these lines

import locale from 'element-ui/lib/locale/lang/ko'

Vue.use(ElementUI, { locale });

And in fact, running vue add element would guide you through all this and automate this process.

Let’s try this out

<slot> is like {children} in react

So we use <slot> in layouts to mean that something will go into there.

@/layout/Default.vue

<template>
    <el-row>
        <el-col :span="24"><slot/></el-col>
    </el-row>
</template>
<script>
export default {
    name: "DefaultLayout"
}
</script>

@/views/Home.vue

<template>
  <DefaultLayout>
  <el-card>
    <div slot="header" class="clearfix">
    <span>Card name</span>
  </div>
  <div v-for="o in 4" :key="o" class="text item">
    {{'List item ' + o }}
  </div>
  </el-card>
  </DefaultLayout>
</template>

<script>
import DefaultLayout from "@/layouts/Default.vue"

export default {
  name: "Home",
  components: {
    DefaultLayout,
  }
}
</script>

@/App.vue

<template>
  <div><router-view></router-view></div>
</template>

<script>
export default{
  name: "App"
}
</script>

<style>
</style>

router-view just renders what is there in the route. Routing configs are done here inside router.js:

import Vue from "vue"
import Router from "vue-router"
import Home from "./views/Home.vue"

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: "/",
      name: "home",
      component: Home
    },
    {
      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(/* webpackChunkName: "about" */ "./views/About.vue")
    }
  ]
})

// You are going to use this in `main.js`

How to Vue.js + Typescript

1. Install typescript configs and Vue.js environments using @vue.cli

2. Typescript basics

Declaring a variable

Every variable has a type.

var data3:number=10;
data3="ddd";
console.log(data3);

As you see, you attach the type after the name of the variable, withy the colon ”:“.

function test1(value:number){
    console.log("value="+value);
}

test1(10);

function test2(value1:number,value2:number):number{
    return value1+value2;
}

var result:string=test2(10,"20");

this one above also. You just attach the type of what’s right just in front.

Function arguments

function test1(value:number){
    console.log("value="+value);
}

Function return value

function test2(value1:number,value2:number):number{
    return value1+value2;
}

Again, just to review, variable type

var result:string=test2(10,"20");

3. Starting off

A part of Helloworld.vue:

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";

@Component
export default class HelloWorld extends Vue {
  @Prop()
  private msg!: string;
}
</script>

You declare things in Vue + Typescript with @ annotation. It’s called a decorator.

Define your template class

You inherit Vue class:

export default class HelloWorld extends Vue

and it’s even simpler to declare class members:

export default class MyComponent extends Vue {
    //data
    public list:string[] = ["a","b","c"]
    private sth:string[] = ["a","b"]
    //props
	@Prop
    private msg!:string;
    
    //computed
    get sampleData(){
            
    }
    //methods
    iAmMethod(){

    }
}

What exactly is a decorator

Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript.

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter.

Class decorator

The one we see above is a class decorator.

A Class Decorator is declared just before a class declaration. The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.

That’s it for now. So this part:

@Component
export default class HelloWorld extends Vue 

would mean that Component decorator would do necessary operations on the class HelloWorld to make it a proper Vue component.

Difference between public and private in this Vue project


Written by Joel Mun. Joel likes Rust, GoLang, Typescript, Wasm and more. He also loves to enlarge the boundaries of his knowledge, mainly by reading books and watching lectures on Youtube. Guitar and piano are necessities at his home.

© Joel Mun 2024