Vue.js Getting Started
Beside Angular and React the colossus frameworks for front-end these days, Vue.js is the last born and it learnt from its famous predecessors.
All these frameworks are great and the choice can be based on the purpose (brand new app, demo, refactoring of a legacy app), the context (team size, degree of collaboration/delegation, practices in place) or simply your taste: in any case you will pick a great framework!
Join me in this article to onboard with Vue.js; we will explore the cli, the standard sections of a Vue component, how to exchange information between child and parent components and finally how to use slots to embed html within another piece of html.
This article has been written using Vue 2 which has the time of the writing is still mainstream and has the widest ecosystem (especially vuetify is not yet compliant with Vue 3).
Installation
Given you have node
already installed, otherwise refer to my post
npm install -g @vue/cli #cli to scaffold your project
You can then create your first project
vue create my-project && cd my-project #keep default settings, especially Vue 2
npm run serve
you may want to change to set `"useTaobaoRegistry": false` in your `~/.vuerc` file
Development Resources
Install VSCode plugin Vetur
Install Vue Devtools Browser extension or standalone if you are developing on Electron platform)
Vue Component
At the core of View.js there is the vue component: the .vue
file. It is one file made of 3 parts that will be split by the framework later:
- an HTML template
- a script part
- a style part
Each part is examplified separately, but remember it is one unique file Finally you can also create pieces of code resuable across your components: they are called mixins
HTML Template
<template>
<div class="content">
<div v-once><h1>{{ pageTitle }}</h1></div>
<!-- Directives -->
<div class="top">
<h2>Core Directives</h2>
<button @click="toggleDisplay()">@click to toggle display</button>
<div>
<span v-if="display">Conditional display with <b>v-if</b></span>
<span v-show="display">Conditional with <b>v-show</b></span>
</div>
<div>
<div><b>v-for</b> example</div>
<table>
<tr>
<th>index</th>
<th>value</th>
</tr>
<tr v-for="(element, index) in collection" :key="index">
<td>{{ index }}</td>
<td>{{ element }}</td>
</tr>
</table>
</div>
</div>
<!-- Child Component -->
<div class="middle">
<h2>Child Component</h2>
<select v-model="selectedColor">
<option disabled value="">Please select one color</option>
<option>red</option>
<option>green</option>
</select>
<br/>
<br/>
<ChildComponent :color="selectedColor" @childEvent="v => childCounter=v"/>
<br/>
<div>Click number <b></b>
</div>
</div>
<!-- About Styles -->
<div class="bottom">
<div>
<h2>Style Examples</h2>
</div>
<button @click="toggleColors()">Toggle borders</button>
<div :style="borderCssStyle">title <code>:style</code>
</div>
<div :style="[borderCssStyle, 'custom-title']">title <code>:style</code> array</div>
<div :class="{'green-border': green}">title <code>:class</code>
</div>
<div :class="[borderCssClass, 'custom-title']">title <code>:class</code> array</div>
</div>
</div>
</template>
Script
The script part is the richest as of course this is were will sit most of the logic.
import ChildComponent from './ChildComponent.vue'
import HelperMixin from './HelperMixin'
function functionX(v) {
console.log("functionX: " + v);
}
export default {
name: "FirstComponent", //best practice: a 2 words name
created() { //cf. lifecycle diagram https://vuejs.org/v2/guide/instance.html
console.log('Component created');
},
components: {ChildComponent},
data() { //note data() is a function
return {
pageTitle: "FirstComponent",
childCounter: 0,
display: true,
collection: ['element1','element2'],
selectedColor: "green",
green: true,
};
},
mixins: [HelperMixin],
computed: {
borderCssStyle() {
return {
border: this.green ?
'1px solid '+this.selectedColor :
'',
};
},
borderCssClass() {
return this.green ? this.selectedColor+'-border' : '';
},
},
methods: {
toggleDisplay() {
functionX("toggleDisplay");
this.display = ! this.display;
},
toggleColors() {
functionX("toggleColors");
this.green = ! this.green;
},
}
};
</script>
Styles
/* global styles, usually only in App.vue only */
<style>
button{
color: white;
background-color: green;
}
</style>
/* scope your style to be applied only in current component */
/* You can use SAAS simply adding lang attribute */
<style scoped>
.content {
align-content: center;
background: linear-gradient(to bottom,white, rgb(203, 235, 203));
background-attachment: fixed;
}
.top {
align-content: center;
}
.custom-title {
font-style: italic;
}
.green-border {
border: 1px solid darkgreen;
}
.red-border {
border: 1px solid red;
}
table {
border: 1px solid black;
display: inline-table;
}
</style>
Mixins
Simply export the code you want to factorise and import that mixin in every component you need it. e.g.: a lifecycle hook
export default {
updated() {
console.log('Component updated');
}
}
Communicating with Child Component
A Child component ChildComponent can be leveraged in a parent one as <ChildComponent/>
. It can also receive properties from its parent
In the other way around the child component can emit event to send back information to its parent.
//in parent component
<ChildComponent :color="colorVariable" alignment="left" @childEvent="v => attributeChild1=v"/>
Note that :color
will be bound to a variable whereas alignment
is the constant value “left” passed to the child, and so does not have the :
.
<template>
<div>
(...)
<ChildComponent :color="selectedColor" alignment="center" @childEvent="v => childCounter=v"/>
</div>
</template>
<script>
export default {
props: ['color','alignment'],
data() {
return { attributeChild1: "any content" };
},
methods: {
sendEvent(){
this.$emit("childEvent", this.attributeChild1);
}
}
};
</script>
<style scoped>
.red div {
color: red;
}
.blue div {
color: blue;
}
.centered div {
text-align: center;
}
.left div {
text-align: left;
}
</style>
You can also add some validation changing the way you declate the props
props: {
color: {
type: String,
required: true,
validator(value) {
return ['blue', 'green','red'].includes(value);
}
},
alignment: {
type: String,
required: true,
}
}
},
CSS inheritance
A parent component can (within scoped CSS) define styles that will be applied to a child component,
<div style="color:red">
<ChildComponent/>
</div>
There is a subtle nuance to understand here: scoped style are not aplied out of the scope but CSS inheritance is still there, for instance here color will be inherited but border style for instance would not be as it is not natively inherited in CSS.
Also the parent component can target CSS classes that are in any level down to it with the deep selector
.class1 >>> .class2 { /* .class1 /deep/ .class2 is the syntax for other CSS pre processors
color: red;
}
Slots
Vue.js slots enables to embed HTML within anoter HTML: the HTML you will provide in the slot
tag will be surrounded by existing and resuable HTML.
Let’s take an example; let’s say I have a nice remark div I want to use at several locations in my app to display some remarks.
I can define a RemarkFrame component as follows inclucing a <slot>
balise. This where the content I want to place will be injected to substitute the slot.
<template>
<div class="remark">
<slot>Default content</slot> <!-- content can be injected here from another component -->
<div>
</template>
<script>
export default ({
name: "RemarkFrame",
data() {
},
})
</script>
<style scoped>
.remark {
padding: 5px;
border: 0.1px solid grey;
background-color: #ddffdd;
}
</style>
I can then use this RemarkFrame element from another component as follows
<template>
<RemarkFrane>
<div>This div comment will be injected in the slot of RemarkFrame</div>
<RemarkFrane>
<RemarkFrane/> <!-- with an empty HTML content the slot will show its default content -->
</template>
<script>
import RemarkFrame from "./RemarkFrame.vue"
export default ({
name: "MyComponent",
data() {
},
components: {RemarkFrame},
})
</script>
<style/>
Directives
Directive | Description | Example |
---|---|---|
class | Apply conditionally a CSS class | <div :class="{'red-border': display}">Title</div> <div :class=[borderCssClass, more]">Title</div> |
style | Apply a parameterised style | <div :style="borderStyle">Title</div> <div :style="[borderStyle, more]">Title</div> |
v-bind | Bind a variable (similar to interpolation with moustache expressions) | <img v-bind:src=""></image> <img :src=""></image> |
v-for | Iterate over an array Do not combine with v-if on same te same element (perf warning) | <tr v-for="(element, index) in collection" :key="index"> |
v-if | Conditional display: adding/removing from DOM | <span v-if="display">Conditionallay</span> |
v-model | Create a two way bindings between an html element valueand a variable | <input v-model="message" placeholder="edit me"> |
v-on | Bind to a method or an event | <button v-on:click="method1()">Click</button> <button @click="method1()">Click</button> |
v-once | Display once only a value not expected to change | <div v-once class="title"></div> |
v-show | Conditional display: via styling display To privilege over v-if if the content is costly to render | <span v-show="display">Conditional</span> |
Enjoy Reading This Article?
Here are some more articles you might like to read next: