Vue is a front-end framework, similar to React. Installation Create a vue project by npm init vue@latest Go to your project cd projectName Install all packages npm i Additionally install SCSS support npm install -D sass-loader sass Run development server npm run dev Install Vetur extension for autocomplete in VSCode Component import
// main.js
import { createApp } from 'vue'
import App from './HelloWorld.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
Component structure
<!-- HelloWorld.vue -->
<!-- view -->
<template>
<h1>Hello {{name}} {{last}}</h1>
<p>It is my first page in <span>Vue</span> which supports SCSS</p>
</template>
<!-- logic -->
<script>
export default {
data() {
return {
<!-- reactive variables -->
name: 'John',
last: 'Smith',
}
}
}
</script>
<!-- styles -->
<style lang="scss" scoped>
p {
color: grey;
span {
color: red;
}
}
</style>
Variable binding with {{ expression }} Only expressions are allowed in double curly braces. if...else , for...loop are not allowed
<h1>Hello {{ variable }} </h1>
<h1>Hello {{ 1 + 1 }} </h1>
<h1>Hello {{ 10 > 5 ? 'Ok' : 'No' }} </h1>
<h1>Hello {{ [1,2,3] }} </h1>
<h1>Hello {{ [1,2,3].map(num => num * 2) }} </h1>
<h1>Hello {{ 1 === 1 && 'that is true' }} </h1>
Life-cycle methods Presented in order of methods trigger
<script>
console.log('start of script')
export default {
data() {
return {
name: 'John',
last: 'Smith',
}
},
beforeCreate() {
// template is not injected, data is available
console.log('beforeCreate')
},
created() {
// template injection is done, but not shown, data is available
console.log('created')
},
beforeMount() {
// before Vue compiling on-fly, connected Vue in script src
console.log('beforeMount')
},
mounted() {
// view is rendered, data is available
console.log('mounted')
},
beforeUnmount() {
console.log('beforeUnmount')
},
unmounted() {
console.log('unmounted')
},
}
console.log('start of script')
</script>
In console we will see following sequence start of script end of script beforeCreate created beforeMount THIS THIS is bind by Vue via proxy to the object returned by data() method. Or to the 'methods' property, hmmm... that is magic.
<script>
export default {
data() {
return {
name: 'John',
last: 'Smith',
}
},
mounted() {
this.hi() // hi
console.log(this.name) // 'John
this.name = 'Kate'
console.log(this.name) // 'Kate
},
methods: {
hi() {
console.log('hi')
}
}
}
</script>
Methods Function go into the 'methods' object property, like in the example above. They are called by this.methodName() . Computed property vs method Method is fired when any reactive variable in the component is changed. Computed property is fired when reactive variable in his formula changes. For other states change computed property uses previously calculated value.
<template>
<h1>Method vs computed property</h1>
<p>Books: <b>{{ author.books }}</b></p>
<p>Do author have books? <b>{{ author.books > 0 ? 'Yes' : 'No' }}</b></p>
<p>Do author have books? <b>{{ doHaveBooksProp }}</b></p>
<p>Do author have books? <b>{{ doHaveBooksFunc() }}</b></p>
<button @click="author.books++">decrement books +1</button>
<button @click="author.books--">increment books -1</button>
<p>Author's age: <b>{{ author.age }}</b></p>
<button @click="author.age++">add age +1</button>
<p>Some number not related to author: <b>{{ num }}</b></p>
<button @click="num++">Add num +1</button>
</template>
<script>
export default {
data() {
return {
author: {
age: 55,
books: 3
},
num: 1,
}
},
methods: {
doHaveBooksFunc() {
// fires when author.books state changes
// fires when author.age state changes
// fires when num state changes
console.log('method is fired')
return this.author.books > 0 ? 'Yes' : 'No'
}
},
computed: {
// a computed getter property
doHaveBooksProp() {
// fires when author.books state changes
console.log('computed property is fired')
return this.author.books > 0 ? 'Yes' : 'No'
}
}
}
</script>
Html directives Special html attributes build by Vue to extend the functionality of a component. v-if Toggles element based on the truthiness of the value
<template>
<button @click="toggleElement">Toggle element</button>
<div v-if="showEl">Hi, I am div</div>
</template>
<script>
export default {
data() {
return {
showEl: false
}
},
methods: {
toggleElement() {
this.showEl = !this.showEl
}
},
}
</script>
v-else
<template>
<button @click="awesome = !awesome">Toggle</button>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
</template>
<script>
export default {
data() {
return {
awesome: true,
}
},
}
</script>
v-else-if
<template>
<div v-if="type === 'A'"> A </div>
<div v-else-if="type === 'B'"> B </div>
<div v-else-if="type === 'C'"> C </div>
<div v-else> Not A/B/C </div>
</template>
<script>
export default {
data() {
return {
type: 'B',
}
},
}
</script>
v-if with template
<template>
Template below
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
</template>
<script>
export default {
data() {
return {
ok: true,
}
},
}
</script>
v-ones Updates only ones, later it stays intact.
<template>
<h1 v-once>Hello {{name}} {{last}}</h1>
<p>It is my first page in <span>Vue</span> which supports SCSS</p>
</template>
<script>
export default {
data() {
return {
name: 'John',
last: 'Smith',
}
},
mounted() {
this.name = 'Kate'
console.log(this.name) // 'Kate' // but html shows 'John' due to 'v-ones' directive
},
methods: {
hi() {
console.log('hi')
}
}
}
</script>
v-html Renders html from string. Tag should be empty.
<template>
<p v-html="rawHtml"></p>
<p>{{rawHtml}}</p>
</template>
<script>
export default {
data() {
return {
name: 'John',
last: 'Smith',
rawHtml: '<b>hi</b>'
}
},
}
</script>
v-show Similar for v-if , but simpler. Component is always rendered with v-show
<template>
<h1 v-show="show">Hello!</h1>
</template>
<script>
export default {
data() {
return {
show: true,
}
},
methods: {},
computed: {},
}
</script>
v-if Vs v-show v-if is "lazy": if the condition is false on initial render, it will not do anything - the conditional block won't be rendered until the condition becomes true for the first time. v-show is simpler, the element is always rendered regardless of initial condition, with CSS-based toggling. v-if has higher toggle costs while v-show has higher initial render costs. v-for Loops over data and renders it. Can be not only array, but even object. Do not forget to specify value for v-bind:key attribute.
<template>
<p>Programming languages</p>
<ul>
<li v-bind:key='lang' v-for="(lang, index) in langsArrOfStr">{{index + 1}}: {{lang}}</li>
</ul>
<p>Programming languages</p>
<ul>
<li v-bind:key='lang' v-for="(lang, index) in langsArrOfObj">{{index + 1}}: {{lang.langName}}</li>
</ul>
<p>Programming languages</p>
<ul>
<li v-bind:key='lang' v-for="(lang) in langsObj">{{lang}}</li>
</ul>
</template>
<script>
export default {
data() {
return {
langsArrOfStr: ['JS', 'C++', 'Java', 'C#', 'ObjectiveC'],
langsArrOfObj : [{langName: 'JS'}, {langName:'C++'}, {langName:'Java'}, {langName:'C#'}, {langName:'ObjectiveC'}],
langsObj: {language: 'JS', creator: 'I do not know', usedIn: 'Web '},
}
},
mounted() {
this.name = 'Kate'
},
methods: {
hi() {
console.log('hi')
}
}
}
</script>
v-for with template
<template>
<ul>
<template v-bind:key="item" v-for="item in items">
<li>{{ item }}</li>
<div class="divider"/>
</template>
</ul>
</template>
<script>
export default {
data() {
return {
items: ['apple', 'lemon', 'melon'],
}
},
}
</script>
<style lang="scss" scoped>
.divider {
height: 1px;
background: grey;
}
</style>
Event handlers Inline
<template>
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
}
</script>
v-on:click We can use v-on:click="methodName" directive in html to pass a method to onClick event.
<template>
<p>Random number: <b>{{randNum}}</b></p>
<button v-on:click="returnRandNum">Random number</button>
</template>
<script>
const randomNumFromTo = (from, to) => Math.floor(Math.random() * (to - from + 1) + from)
export default {
data() {
return {
randNum: randomNumFromTo(1, 100),
}
},
methods: {
returnRandNum() {
this.randNum = randomNumFromTo(1, 100)
}
}
}
</script>
@click Or we can use shorter version @click which is nicer for eyes.
<button @click="returnRandNum">Random number</button>
Event object Event object is automatically received by event handler.
<template>
<button @click="greet">Greet</button>
</template>
<script>
export default {
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(e) {
alert(`Hello ${this.name}!`)
if (e) alert(e.target.tagName)
}
},
computed: {},
}
</script>
Calling methods in inline handlers
<template>
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>
</template>
<script>
export default {
data() {
return {
name: 'Vue.js'
}
},
methods: {
say(message) {
alert(message)
}
},
}
Event object in inline handlers
<template>
<button @click="warn('Form cannot be submitted yet.', $event)"> Submit </button>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
warn(message, event) {
if (event) event.preventDefault()
alert(message)
}
},
}
</script>
Event modifiers .stop event's propagation will be stopped .prevent submit event will no longer reload the page .self trigger handler if event.target is the element itself, i.e. not from a child element .capture capture mode .once triggered at most once .passive scroll event's default behavior (scrolling) will happen Modifiers can be chained or can be called without handler
<a @click.stop="doThis"></a>
<form @submit.prevent="onSubmit"></form>
<a @click.stop.prevent="doThat"></a>
<form @submit.prevent></form>
<div @click.self="doThat">...</div>
<div @click.capture="doThis">...</div>
<a @click.once="doThis"></a>
Key modifiers On keyboard events, we may listen and fire function on specific keys. Most commonly used keys .enter .tab .delete .esc .space .up .down .left .right .ctrl .alt .shift .meta .exact Any valid key names are applicable exposed via KeyboardEvent.key as modifiers by converting them to kebab-case.
<input @keyup.enter="submit" />
<input @keyup.page-down="onPageDown" />
<input @keyup.alt.enter="clear" />
<div @click.ctrl="doSomething">Do something</div>
<!-- this will fire even if Alt or Shift is also pressed -->
<button @click.ctrl="onClick">A</button>
<!-- this will only fire when Ctrl and no other keys are pressed -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- this will only fire when no system modifiers are pressed -->
<button @click.exact="onClick">A</button>
Mouse button modifiers .left .middle .right Event will be fired only when we hit space.
<template>
<input :value="inputVal" @keyup.space="updateInputVal($event)" />
<div> Value from input: <b>{{ inputVal }}</b> </div>
</template>
<script>
export default {
data() {
return {
inputVal: '',
}
},
methods: {
updateInputVal(e) {
this.inputVal = e.target.value
},
},
}
</script>
Before hit space After hit space Binding v-bind:value="expression" To dynamically attach a value to an attribute we have to use v-bind:value="expression" :value="expression" Shorter version :value="expression"
<template>
<input value="regular html attr value" /> <br><br>
<input value={{randNum}} /> <br><br>
<input v-bind:value="5 + 5"> <br><br>
<input :value="randNum">
</template>
<script>
const randomNumFromTo = (from, to) => Math.floor(Math.random() * (to - from + 1) + from)
export default {
data() {
return {
randNum: randomNumFromTo(1, 100),
}
},
methods: {
returnRandNum() {
this.randNum = randomNumFromTo(1, 100)
}
}
}
</script>
Multiple attributes binding
<template>
<input v-bind="objectOfAttrs" value="v:bind='objectOfAttrs'" /><br>
</template>
<script>
export default {
data() {
return {
objectOfAttrs: {
id: 'inp',
class: 'basic'
}
}
},
methods: {
},
}
</script>
Two way binding - long way
<template>
<input :value="inputVal" @input="updateInputVal($event)"/>
<div>Value from input: <b>{{inputVal}}</b></div>
</template>
<script>
export default {
data() {
return {
inputVal: '',
}
},
methods: {
updateInputVal(e) {
this.inputVal = e.target.value
}
}
}
</script>
Two way binding - short way v-model
<template>
<input v-model="inputVal" />
<div>Value from input: <b>{{inputVal}}</b></div>
</template>
<script>
export default {
data() {
return {
inputVal: '',
}
},
methods: {
}
}
</script>
Disabled element
<template>
<input :disabled=true value=":disabled=true" /><br>
<input :disabled=false value=":disabled=false" /><br>
<input :disabled="truthyVal" value=":disabled='truthyVal'" /><br>
<input :disabled="falsyVal" value=":disabled='falsyVal'" /><br>
</template>
<script>
export default {
data() {
return {
truthyVal: true,
falsyVal: false,
}
},
}
</script>
Input General
<template>
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />
</template>
<script>
export default {
data() {
return {
message: '',
}
},
}
</script>
Textarea
<template>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
</template>
<script>
export default {
data() {
return {
message: '',
}
},
}
</script>
Checkbox
<template>
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
</template>
<script>
export default {
data() {
return {
checked: true,
}
},
}
</script>
Checkboxes
<template>
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
</template>
<script>
export default {
data() {
return {
checkedNames: []
}
},
}
</script>
Radio
<template>
<div>Picked: {{ picked }}</div>
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
</template>
<script>
export default {
data() {
return {
picked: 'One'
}
},
}
</script>
Select
<template>
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</template>
<script>
export default {
data() {
return {
selected: ''
}
},
methods: {
}
}
</script>
Multiple select
<template>
<div>Selected: {{ selected }}</div>
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</template>
<script>
export default {
data() {
return {
selected: []
}
},
}
</script>
Modifiers
<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" />
<!-- converts to number -->
<input v-model.number="age" />
<!-- trims white spaces -->
<input v-model.trim="msg" />
Access updated DOM Use the nextTick() to wait for the DOM update to complete after a state change
import { nextTick } from 'vue'
export default {
methods: {
increment() {
this.count++
nextTick(() => {
// access updated DOM
})
}
}
}
DOM reference Ref
<template>
<input ref="input">
</template>
<script>
export default {
data() {
return {
}
},
mounted() {
this.$refs.input.focus()
}
}
</script>
Refs inside v-for ref used inside v-for gives an array containing the corresponding elements Elements order inside an array can be different.
<template>
<ul>
<li v-bind:key="item" v-for="item in list" ref="items"> {{ item }} </li>
</ul>
</template>
<script>
export default {
data() {
return {
list: [
'abb', 'siemens', 'GE', 'Alstom'
]
}
},
mounted() {
console.log(this.$refs.items) // (4) [li, li, li, li]
}
}
</script>
Watchers With a watcher we may trigger a function on a state change.
<template>
<p>Ask a yes/no question: <input v-model="question" /></p>
<p>{{ answer }}</p>
</template>
<script>
export default {
data() {
return {
question: '',
answer: 'Questions usually contain a question mark. ;-)'
}
},
watch: {
// whenever question changes, this function will run
question(newQuestion, oldQuestion) {
if (newQuestion.indexOf('?') > -1) this.getAnswer()
}
},
methods: {
async getAnswer() {
this.answer = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
this.answer = (await res.json()).answer
} catch (e) {
this.answer = 'Error! Could not reach the API. ' + error
}
}
}
}
Components Export & import Child component ButtonComponent.vue
<template>
<button @click="sayHi">{{buttonText}}</button>
</template>
<script>
export default {
props: ['buttonText'],
data() {
return {
someVar: '',
}
},
methods: {
sayHi() {
alert('hello from ---' + this.buttonText + '--- button')
},
},
}
</script>
<style lang="scss" scoped>
button {
padding: 10px;
margin: 10px;
background: black;
color: white;
}
</style>
Parent component
<template>
<h1>I am parent component</h1>
<ButtonComponent buttonText='Click me' />
<ButtonComponent buttonText='Press on me'/>
</template>
<script>
import ButtonComponent from './ButtonComponent.vue'
export default {
components: { ButtonComponent },
data() {
return {
someVar: '',
}
},
methods: {
someFunc() {
},
},
}
</script>
Imported components should be registered in components {} object. Components from array
<template>
<h1>I am parent component</h1>
<ButtonComponent
v-for="text in bntTexts"
:key="text"
:buttonText="text"
/>
</template>
<script>
import ButtonComponent from './ButtonComponent.vue'
export default {
components: { ButtonComponent },
data() {
return {
bntTexts: ['Click me', 'Press on me'],
}
},
methods: {
someFunc() {
},
},
}
</script>
Emit We can emit data from a child component on listen for it and assign a handler on parent component. Child
<template>
<button @click="$emit('msgFromChild', 'hello from child')">
Emit from child to parent
</button>
</template>
<script>
export default {
data() {
},
methods: {
},
}
</script>
Parent
<template>
<h1>Parent</h1>
<Child @msgFromChild="readMessage"/>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
data() {
},
methods: {
readMessage(args) {
alert(args)
},
},
}
</script>
Slots (props.children) <slot> is a placeholder where we want the content to go when we pass it between component tags. Parent
<template>
<h1>Parent</h1>
<Child>Hello, I am a child component</Child>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
data() {
},
methods: {
},
}
</script>
Child
<template>
<p>Passed content goes below</p>
<b><slot /></b>
</template>
<script>
export default {
props: [],
data() {
},
methods: {
},
}
</script>
Dynamic component <component> tag with the special :is="'componentName'" attribute can dynamically add a component with such name.
<template>
<h1>Parent</h1>
<component :is="'Child1'"></component>
<component :is="componentToShow"></component>
</template>
<script>
import Child1 from './Child1.vue'
import Child2 from './Child2.vue'
import Child3 from './Child3.vue'
export default {
components: { Child1, Child2, Child3 },
data() {
return {
componentToShow: 'Child3',
}
},
methods: {
myFunc() {
},
},
}
</script>