Introduction to Vue 3
Vue 3 introduces a reactive framework that simplifies building user interfaces using a declarative paradigm. Unlike imperative programming (e.g., direct DOM manipulation), Vue allows developers to describe "what" the UI should look like, and the framework handles the "how."
Vue follows the Model-View-ViewModel (MVVM) pattern, where the ViewModel acts as a bridge between the data (Model) and the visual interface (View).
Getting Started with CDN
quickest way to prototype with Vue 3 is via a CDN link. The application is initialized using Vue.createApp, which accepts an options object containing data, methods, and lifecycle hooks.
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue 3 Setup</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app"></div>
<script>
const App = {
template: `
<div>
<h2>{{ pageTitle }}</h2>
<button @click="updateTitle">Update Text</button>
</div>
`,
data() {
return {
pageTitle: "Hello Vue 3"
}
},
methods: {
updateTitle() {
this.pageTitle = "Vue 3 is Awesome";
}
}
};
Vue.createApp(App).mount("#app");
</script>
</body>
</html>
Template Definition
Templates can be defined directly in JavaScript or using HTML tags. Using a <template> tag or a script block with type="x-template" keeps the HTML structure clean and separates logic from the view.
Essential Directives
Text Interpolation and Mustache Syntax
The mustache syntax {{ }} is used for text interpolation. It can evaluate JavaScript expressions, access data properties, or call methods.
<div>{{ userMessage }}</div>
<!-- Outputting raw text -->
<div v-text="userMessage"></div>
Directives for Content Control
- v-once: Renders the element and components once only. Subsequent data changes will not update the content.
- v-html: Parses the string as raw HTML. Use with caution to avoid XSS attacks.
- v-pre: Skips compilation for this element and its children. Useful for displaying raw mustache syntax.
- v-cloak: Keeps the element in the DOM until the associated Vue instance finishes compilation. Often used with CSS
[v-cloak] { display: none; }to hide uncompiled mustache tags.
Handling Attributes and Events
Dynamic Binding with v-bind
v-bind (shorthand :) dynamically binds one or more attributes to an expression.
<!-- Dynamic Image Source -->
<img :src="imageURL" :alt="imageDesc" />
<!-- Dynamic Class Binding (Object Syntax) -->
<div :class="{ active: isActive, error: hasError }">Status</div>
<!-- Dynamic Style Binding (Array Syntax) -->
<div :style="[baseStyles, overridingStyles]">Styled Content</div>
<!-- Dynamic Attribute Name -->
<button :[dynamicAttribute]="value">Submit</button>
Event Handling with v-on
v-on (shorthand @) listens to DOM events and executes JavaScript.
<!-- Basic Click Handler -->
<button @click="handleClick">Click Me</button>
<!-- Passing Arguments -->
<button @click="handleDelete(id, $event)">Delete</button>
<!-- Event Modifiers -->
<form @submit.prevent="onSubmit">...</form>
<div @click.stop="doThis">Stop Propagation</div>
Conditional Rendering
Vue provides v-if, v-else-if, and v-else for conditional rendering based on the truthiness of the expression.
<div v-if="isLoggedIn">Welcome back!</div>
<div v-else-if="isGuest">Please log in.</div>
<div v-else>Access Denied</div>
For toggling visibility frequently, v-show is preferable as it simply toggles the CSS display property, whereas v-if performs actual DOM insertion and removal.
List Rendering
The v-for directive requires a specific syntax of item in items.
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.name }}
</li>
</ul>
The Importance of Keys
When using v-for, providing a unique key attribute is crucial for Vue's Virtual DOM diffing algorithm. Keys help Vue identify which nodes have changed, are added, or are removed, allowing for efficient re-rendering. Without keys, Vue may attempt to patch elements in place to maximize efficiency, which can lead to UI bugs with stateful components or input values.
Options API: Computed and Watch
Computed Properties
Computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when one of its dependencies changes. This makes them more efficient than methods for expensive calculations.
computed: {
formattedName() {
return `${this.firstName} ${this.lastName}`;
},
fullName: {
get() {
return this.firstName + ' ' + this.lastName;
},
set(newValue) {
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[names.length - 1];
}
}
}
Watchers
Watchers allow performing asynchronous operations or expensive logic in response to changing data.
watch: {
// Simple watcher
sourceData(newVal, oldVal) {
console.log(`Changed from ${oldVal} to ${newVal}`);
},
// Watcher with options
deepObject: {
handler(val) { /* ... */ },
deep: true, // Detect nested changes
immediate: true // Trigger immediately on creation
}
}
Form Input Bindings
The v-model directive creates a two-way binding on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type.
<!-- Text Input -->
<input v-model="message" placeholder="Edit me">
<!-- Checkbox -->
<input type="checkbox" id="checkbox" v-model="checked">
<!-- Multiple Checkboxes (bound to Array) -->
<input type="checkbox" value="Vue" v-model="checkedNames">
<input type="checkbox" value="React" v-model="checkedNames">
<!-- Radio Buttons -->
<input type="radio" value="One" v-model="picked">
<input type="radio" value="Two" v-model="picked">
<!-- Select Dropdown -->
<select v-model="selected">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
Modifiers
- .lazy: Syncs input after
changeevents instead ofinput. - .number: Automatically casts the input string to a number.
- .trim: Trims whitespace from the input automatically.
Component Basics
Global vs. Local Registration
Components can be registered globally, making them available in any template within the Vue application, or locally, scoped only to the parent component where they are imported.
Component Naming Conventions
When defining components in JavaScript, PascalCase (e.g., MyComponent) is standard. However, in HTML templates, kebab-case (e.g., <my-component>) is required because HTML tags are case-insensitive.