Learning vue
Contents
Creating a project
npm create vite@latest <APPLICATION_NAME> -- --template vue
Template Syntax
Attribute Bindings
<span>Message: {{ msg }}</span>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
<div :id="dynamicId"></div>
<!-- same as :id="id" -->
<div :id></div>
<button :disabled="isButtonDisabled">Button</button>
// JS Expressions
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
v-show="[alertText.length > 0]"
<a v-bind:href="url"> ... </a>
<a :[attributeName]="url"> ... </a>
<span v-text="msg"></span>
<div v-html="html"></div>
<div v-cloak>
{{ message }}
</div>
Dynamically Binding Multiple Attributes
If you have a JavaScript object representing multiple attributes that looks like this:
const objectOfAttrs = {
id: 'container',
class: 'wrapper',
style: 'background-color:green'
}
You can bind them to a single element by using v-bind without an argument:
<div v-bind="objectOfAttrs"></div>
Directives
<p v-if="seen">Now you see me</p> <a @click="doSomething"> ... </a> // <a v-on:click="doSomething"> ... </a> <form @submit.prevent="onSubmit">...</form>
The .prevent modifier tells the v-on directive to call event.preventDefault() on the triggered event
Reactivity Fundamentals
ref() takes the argument and returns it wrapped within a ref object with a .value property:
import { ref } from 'vue'
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
<div>{{ count }}</div>
Notice that we did not need to append .value when using the ref in the template. For convenience, refs are automatically unwrapped when used inside templates
For more complex logic, we can declare functions that mutate refs in the same scope and expose them as methods alongside the state:
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
function increment() {
// .value is needed in JavaScript
count.value++
}
// don't forget to expose the function as well.
return {
count,
increment
}
}
}
Exposed methods can then be used as event handlers:
<button @click="increment">
{{ count }}
</button>
<script setup>
Manually exposing state and methods via setup() can be verbose. Luckily, it can be avoided when using Single-File Components (SFCs). We can simplify the usage with <script setup>:
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }}
</button>
</template>
Deep Reactivity
A ref will make its value deeply reactive. This means you can expect changes to be detected even when you mutate nested objects or arrays:
import { ref } from 'vue'
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply() {
// these will work as expected.
obj.value.nested.count++
obj.value.arr.push('baz')
}
DOM Update Timing
When you mutate reactive state, the DOM is updated automatically. However, it should be noted that the DOM updates are not applied synchronously. Instead, Vue buffers them until the "next tick" in the update cycle to ensure that each component updates only once no matter how many state changes you have made.
To wait for the DOM update to complete after a state change, you can use the nextTick() global API:
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// Now the DOM is updated
}
reactive()
There is another way to declare reactive state, with the reactive() API. Unlike a ref which wraps the inner value in a special object, reactive() makes an object itself reactive:
import { reactive } from 'vue'
const state = reactive({ count: 0 })
Usage in template:
template
<button @click="state.count++">
{{ state.count }}
</button>
Limitations
- Limited value types: it only works for object types (objects, arrays, and collection types such as Map and Set). It cannot hold primitive types such as string, number or boolean.
- Cannot replace entire object
- Not destructure-friendly
Due to these limitations, we recommend using ref() as the primary API for declaring reactive state.
Computed Properties
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// a computed ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
Here we have declared a computed property publishedBooksMessage. The computed() function expects to be passed a getter function, and the returned value is a computed ref. Similar to normal refs, you can access the computed result as publishedBooksMessage.value
Writable Computed
You can create one by providing both a getter and a setter:
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// Note: we are using destructuring assignment syntax here.
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
== Class and Style Bindings ==
<pre>
<div :class="{ active: isActive }"></div>
The above syntax means the presence of the active class will be determined by the truthiness of the data property isActive.
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
<div :class="[activeClass, errorClass]"></div>
Conditional Rendering
Prefer v-show if you need to toggle something very often, and prefer v-if if the condition is unlikely to change at runtime.
<h1 v-if="awesome">Vue is awesome!</h1> <h1 v-else>Oh no 😢</h1> <div v-else-if="type === 'B'"> <template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> <h1 v-show="ok">Hello!</h1>
List Rendering
<li v-for="item in items">
{{ item.message }}
</li>
<li v-for="(item, index) in items">
{{ index }} - {{ item.message }}
</li>
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
<span v-for="n in 10">{{ n }}</span>
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
Event Handling
function say(message) {
alert(message)
}
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>
<form @submit.prevent="onSubmit"></form>
<form @submit.prevent></form>
<a @click.stop="doThis"></a>
<input @keyup.enter="submit" />
<input @keyup.page-down="onPageDown" />
.exact modifier
<!-- 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>
Form Bindings
<input :value="text" @input="event => text = event.target.value"> <input v-model="text">
- <input> with text types and <textarea> elements use value property and input event;
- <input type="checkbox"> and <input type="radio"> use checked property and change event;
- <select> uses value as a prop and change as an event.
<input v-model="message" placeholder="edit me" />
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
const checkedNames = ref([])
<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>
<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>
<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>
<input v-model.lazy="msg" />
<input v-model.number="age" />
<input v-model.trim="msg" />
Lifecycle Hooks
onMounted hook can be used to run code after the component has finished the initial rendering and created the DOM nodes:
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
console.log(`the component is now mounted.`)
})
</script>
There are also other hooks which will be called at different stages of the instance's lifecycle, with the most commonly used being onMounted, onUpdated, and onUnmounted.
Click Here to See Lifecycle Diagram
Options API
State
export default {
data() {
return { a: 1 }
},
created() {
console.log(this.a) // 1
console.log(this.$data) // { a: 1 }
}
}
export default {
props: ['size', 'myMessage']
}
export default {
props: {
// type check
height: Number,
// type check plus other validations
age: {
type: Number,
default: 0,
required: true,
validator: (value) => {
return value >= 0
}
}
}
}
export default {
data() {
return { a: 1 }
},
computed: {
// readonly
aDouble() {
return this.a * 2
},
// writable
aPlus: {
get() {
return this.a + 1
},
set(v) {
this.a = v - 1
}
}
},
}
export default {
data() {
return { a: 1 }
},
methods: {
plus() {
this.a++
}
},
}