0% found this document useful (0 votes)
25 views109 pages

Vuejs

This is the document

Uploaded by

tyagismriti966
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views109 pages

Vuejs

This is the document

Uploaded by

tyagismriti966
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 109

1. Introduction to Vue.

js and Its Ecosystem


What is Vue.js?
Vue.js is a progressive JavaScript framework used to build user interfaces and
single-page applications (SPAs). It was created by Evan You and first released in
2014. Vue’s design focuses on being incrementally adoptable, meaning
developers can use as much or as little of the framework as needed. This makes it
suitable for projects ranging from small interactive widgets to complex web
applications.
Vue combines the best ideas from frameworks like React and Angular while
keeping things simple. It offers a reactive data-binding system, component-based
architecture, and an intuitive templating syntax. One of its key strengths is its
gentle learning curve, allowing developers to get started quickly while providing
advanced features for building complex applications.

Core Principles of Vue.js


1. Reactive Data Binding: Vue’s reactivity system ensures that the UI
automatically updates when data changes.
2. Component-Based Architecture: Applications are divided into reusable,
self-contained components, making code more modular and maintainable.
3. Declarative Rendering: You describe what you want the UI to look like, and
Vue takes care of rendering it efficiently.
4. Virtual DOM: Vue maintains a lightweight virtual representation of the
DOM, optimizing performance by minimizing direct manipulation of the
actual DOM.
5. Two-Way Data Binding: The v-model directive allows easy synchronization
between form inputs and application state.
6. Directives: Special attributes like v-if and v-for provide powerful tools to
control the structure of the DOM directly in templates.

Vue.js Versions
• Vue 2: The older version that introduced concepts like the Options API.
• Vue 3: Introduced the Composition API, better TypeScript support, and
performance improvements. Vue 3 is now the recommended version for
new projects.

Why Choose Vue.js?


1. Simplicity: The core library focuses on the view layer, making it simple to
integrate with other projects.
2. Flexibility: Incremental adoption allows using Vue in parts of an app
without rewriting everything.
3. Rich Ecosystem: Tools like Vue Router and Pinia simplify common
development tasks.
4. Active Community: Extensive documentation and community support
make learning and troubleshooting easy.

Vue.js Ecosystem
Vue’s ecosystem consists of several official libraries and tools that simplify
different aspects of development:
1. Vue Router
Manages navigation and URL handling in SPAs. It enables defining routes
and dynamically rendering components based on the current route.
2. State Management (Vuex / Pinia)
o Vuex: Centralized state management, commonly used in larger
applications.
o Pinia: The recommended state management solution in Vue 3,
providing a simpler API and better TypeScript support.
3. Vue DevTools
A browser extension that allows developers to inspect the component tree,
monitor state changes, and track events, making debugging easier.
4. Build Tools (Vue CLI and Vite)
o Vue CLI: A command-line tool for scaffolding projects, managing
dependencies, and configuring build processes.
o Vite: A fast build tool optimized for modern development, especially
with Vue 3. It offers instant server startup and Hot Module
Replacement (HMR).
5. Nuxt.js
A framework for server-side rendering (SSR), static site generation (SSG),
and improving SEO. It simplifies handling complex features like middleware,
authentication, and API integration.
6. Testing Tools
o Vue Test Utils: Official unit testing library.
o Jest / Cypress: Popular choices for unit and end-to-end testing in Vue
projects.
7. Community and Plugins
The Vue community has built countless plugins that cover everything from
form validation to animation libraries. Official forums, Discord servers, and
GitHub repositories offer support and collaboration opportunities.

Real-World Applications of Vue.js


1. Small Projects: Embedding interactive components into existing websites.
2. Medium Projects: Building SPAs for dashboards, admin panels, and content
management systems.
3. Large Projects: Creating enterprise-level applications with complex state
management, modular architecture, and optimized performance.
Popular companies using Vue.js include Alibaba, Xiaomi, and GitLab, highlighting
its ability to handle large-scale applications.

2. Setting Up a Vue Project (Vue CLI and Vite)


When starting a new Vue.js project, you have two primary tools to choose from:
Vue CLI and Vite. Both help streamline the setup process by providing pre-
configured environments, but they have different focuses.
Vue CLI (Command Line Interface)
Vue CLI is the traditional tool for scaffolding Vue.js projects. It offers a rich set of
features, including plugins, presets, and a GUI for managing projects.
Installing Vue CLI
First, install Vue CLI globally:
shCopyEditnpm install -g @vue/cli
Verify the installation:
shCopyEditvue --version
Creating a Project with Vue CLI
Use the vue create command to create a new project:
shCopyEditvue create my-vue-app
You’ll be prompted to choose between default settings and manual configuration.
The manual option lets you pick features like:
• Babel
• TypeScript
• Vuex
• Router
• Linter/Formatter
Once the project is created, navigate into the project folder:
shCopyEditcd my-vue-app
Install dependencies:
shCopyEditnpm install
Start the development server:
shCopyEditnpm run serve
The app will run on http://localhost:8080/ by default.
Vite (Recommended for Vue 3)
Vite is a modern build tool that leverages native ES modules, providing faster
startup and Hot Module Replacement (HMR). Vite is now the recommended
choice for Vue 3 applications.
Installing Vite
You can create a new Vite project directly using the following command:
shCopyEditnpm create vite@latest my-vue-app --template vue
This initializes a new project with Vue as the template.
Navigate into your project:
shCopyEditcd my-vue-app
Install dependencies:
shCopyEditnpm install
Start the development server:
shCopyEditnpm run dev
By default, the app runs on http://localhost:5173/.

Project Structure
Both Vue CLI and Vite create a similar project structure:
cssCopyEditmy-vue-app/
├── node_modules/
├── public/
│ └── index.html
├── src/
│ ├── App.vue
│ ├── main.js (or main.ts for TypeScript)
│ └── components/
└── package.json
• public/: Contains static assets like images and the index.html file.
• src/: Contains the source code for your app.
• App.vue: The root component of the application.
• main.js: The entry point where the Vue app is created and mounted.

Configuring the Project


Vue CLI Configuration
Vue CLI uses a vue.config.js file for custom configurations, like changing the dev
server port or adding custom build steps:
jsCopyEditmodule.exports = {
devServer: {
port: 3000
}
}
Vite Configuration
Vite uses a vite.config.js or vite.config.ts file. Here’s a simple example to change
the port:
jsCopyEditimport { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
port: 3000
}
})

Comparing Vue CLI and Vite


Feature Vue CLI Vite
Build Tool Webpack Native ESM
Development Speed Slower Instant reloads
HMR (Hot Module Replacement) Medium speed Very fast
Configuration Complex Simple
Feature Vue CLI Vite
TypeScript Support Good Excellent
Recommended For Large apps Modern Vue 3 apps

Vite is often preferred for its simplicity, speed, and compatibility with Vue 3. For
legacy projects or more complex configurations, Vue CLI may still be a better fit.

3. Vue Instance and App Initialization


The foundation of any Vue.js application lies in creating and managing the Vue
instance. In Vue 3, this process involves setting up an app with the createApp()
function. Let’s explore the details step by step.

What is the Vue Instance?


A Vue instance is the core of a Vue application. It acts as the bridge between the
view (template/DOM) and the model (data/state). It handles rendering, event
handling, and reactivity.
In Vue 2, you created a new Vue instance directly with new Vue(). In Vue 3, the
process is more modular, using createApp().

Initializing a Vue App (Vue 3)


1. Project Structure Overview
When you create a new Vue project with Vite, the structure looks like this:
cssCopyEditsrc/
├── App.vue
├── main.js (or main.ts for TypeScript)
└── components/
1. main.js (or main.ts)
This file is the entry point of your application, where the Vue app is created and
mounted to the DOM.
Example main.js:
javascriptCopyEdit// Import createApp from vue
import { createApp } from 'vue'
// Import the root App component
import App from './App.vue'
// Create the Vue app instance
const app = createApp(App)
// Mount the app to the #app element in index.html
app.mount('#app')
In this code:
• createApp() creates a new Vue app instance.
• mount('#app') attaches the app to the element with the ID app in
index.html.
1. index.html
This file contains the container where Vue will mount the app.
htmlCopyEdit<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
When Vue mounts the app, it takes control of everything inside the #app div.
1. App.vue
App.vue is the root component. All other components will be nested inside this.
htmlCopyEdit<template>
<h1>{{ message }}</h1>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
}
}
}
</script>
When the app is initialized, this template is rendered inside the #app div.

Options API vs Composition API


Vue 3 introduced the Composition API as an alternative to the traditional Options
API. Let’s look at both approaches.
Options API
The Options API organizes logic into sections like data, methods, computed, and
watch.
javascriptCopyEditexport default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
Composition API
The Composition API organizes logic into a single setup() function. It provides
more flexibility and better reusability.
javascriptCopyEditimport { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
}
• ref() makes the variable reactive.
• count.value is used to access and update the reactive value.

Passing Options to createApp()


You can pass plugins, global components, or custom options when creating the
app:
javascriptCopyEditimport { createApp } from 'vue'
import App from './App.vue'
import MyPlugin from './plugins/MyPlugin'
const app = createApp(App)
// Use plugins or register components globally
app.use(MyPlugin)
app.mount('#app')

Lifecycle Hooks
Vue provides lifecycle hooks that let you execute code at different stages of a
component’s life cycle.
Example:
javascriptCopyEditexport default {
mounted() {
console.log('Component has been mounted!')
}
}
Common lifecycle hooks:
• created — Runs after the instance is created.
• mounted — Runs after the component is mounted to the DOM.
• updated — Runs after data changes and the DOM updates.
• unmounted — Runs when the component is removed from the DOM.

4. Template Syntax and Data Binding in Vue.js


Vue’s template syntax allows you to build dynamic UIs by binding data to the
DOM using declarative expressions. In this section, we’ll cover the various ways
you can bind data, handle events, and use Vue’s powerful directives.

What is Template Syntax?


Template syntax in Vue is a special set of expressions that let you embed
JavaScript logic directly into your HTML templates. These templates are compiled
into virtual DOM render functions behind the scenes.
Vue provides two main types of bindings:
• Interpolation for text content.
• Directives for manipulating the DOM.
Let’s go over each one in detail.

1. Interpolation
Interpolation is the process of embedding expressions into your templates using
double curly braces ({{ }}).
Example:
htmlCopyEdit<template>
<h1>{{ message }}</h1>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
}
}
}
</script>
You can use simple JavaScript expressions inside the curly braces:
htmlCopyEdit<p>{{ 5 + 5 }}</p> <!-- 10 -->
<p>{{ message.toUpperCase() }}</p> <!-- HELLO VUE! -->
Binding Raw HTML
By default, Vue escapes HTML content to prevent XSS attacks. If you want to
render raw HTML, use the v-html directive:
htmlCopyEdit<template>
<div v-html="rawHtml"></div>
</template>
<script>
export default {
data() {
return {
rawHtml: '<strong>Bold Text</strong>'
}
}
}
</script>

2. Attribute Binding with v-bind


To bind attributes like src, href, or class, use the v-bind directive. The shorthand
for v-bind is :.
htmlCopyEdit<template>
<img :src="imageUrl" alt="Vue Logo">
<a :href="website">Visit Website</a>
</template>
<script>
export default {
data() {
return {
imageUrl: 'https://vuejs.org/images/logo.png',
website: 'https://vuejs.org'
}
}
}
</script>
You can also bind multiple attributes at once using an object:
htmlCopyEdit<template>
<div v-bind="attrs"></div>
</template>
<script>
export default {
data() {
return {
attrs: {
id: 'container',
class: 'box'
}
}
}
}
</script>

3. Two-Way Data Binding with v-model


v-model creates a two-way binding between form inputs and the app’s data.
htmlCopyEdit<template>
<input v-model="name" placeholder="Enter your name">
<p>{{ name }}</p>
</template>
<script>
export default {
data() {
return {
name: ''
}
}
}
</script>
This works with:
• input
• textarea
• select
• checkbox and radio groups
Example with checkboxes:
htmlCopyEdit<template>
<input type="checkbox" v-model="isChecked">
<p>Checked: {{ isChecked }}</p>
</template>
<script>
export default {
data() {
return {
isChecked: false
}
}
}
</script>

4. Event Binding with v-on


Use v-on to listen to DOM events. The shorthand for v-on is @.
htmlCopyEdit<template>
<button @click="increment">Click Me</button>
<p>{{ count }}</p>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
You can pass arguments to event handlers:
htmlCopyEdit<button @click="greet('Vue')">Greet</button>
<script>
export default {
methods: {
greet(name) {
alert('Hello ' + name)
}
}
}
</script>
Event Modifiers
Vue provides modifiers to simplify common tasks:
• .prevent — Prevent default action
• .stop — Stop event propagation
• .once — Execute handler only once
Example:
htmlCopyEdit<form @submit.prevent="submitForm">
<button type="submit">Submit</button>
</form>

5. Class and Style Binding


You can dynamically apply classes and inline styles with v-bind.
Class Binding
Bind classes based on conditions:
htmlCopyEdit<template>
<div :class="{ active: isActive, 'text-danger': hasError }">Hello</div>
</template>
<script>
export default {
data() {
return {
isActive: true,
hasError: false
}
}
}
</script>
Style Binding
Inline styles can also be bound:
htmlCopyEdit<template>
<div :style="{ color: textColor, fontSize: fontSize + 'px' }">Styled Text</div>
</template>
<script>
export default {
data() {
return {
textColor: 'blue',
fontSize: 20
}
}
}
</script>

6. Conditional Rendering
You can conditionally render elements using v-if and v-show.
htmlCopyEdit<template>
<p v-if="isVisible">This is visible</p>
<button @click="isVisible = !isVisible">Toggle</button>
</template>
<script>
export default {
data() {
return {
isVisible: true
}
}
}
</script>

7. List Rendering
Use v-for to loop over arrays or objects.
htmlCopyEdit<template>
<ul>
<li v-for="(item, index) in items" :key="index">
{{ item }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: ['Apple', 'Banana', 'Orange']
}
}
}
</script>

8. Computed Properties
For more complex logic, use computed properties:
htmlCopyEdit<template>
<p>{{ fullName }}</p>
</template>
<script>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
}
</script>

9. Watchers
Watchers are useful for tracking changes to data:
htmlCopyEdit<template>
<input v-model="name">
</template>
<script>
export default {
data() {
return {
name: ''
}
},
watch: {
name(newValue, oldValue) {
console.log('Name changed from', oldValue, 'to', newValue)
}
}
}
</script>

5. Methods and Event Handling in Vue.js


In Vue.js, methods and event handling play a crucial role in making your
application interactive and dynamic. Methods are functions that define the
actions your components can perform, while event handling allows you to listen
for user actions like clicks, input changes, form submissions, and more.
Let’s dive deep into these concepts and see how they work with practical
examples.

🚦 1. Defining Methods
In Vue, you define methods inside the methods option of a component. Methods
are typically used to handle events, perform calculations, or manipulate data.
Example:
htmlCopyEdit<template>
<button @click="incrementCount">Click Me</button>
<p>Count: {{ count }}</p>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
incrementCount() {
this.count++
}
}
}
</script>
In this example:
• incrementCount is a method that increases the count when the button is
clicked.
• @click="incrementCount" binds the click event to the method.

📌 2. Event Handling with v-on


Vue uses the v-on directive to listen for DOM events. The shorthand for v-on is @.
Basic Event Handling
htmlCopyEdit<template>
<button v-on:click="greet">Greet</button>
</template>
<script>
export default {
methods: {
greet() {
alert('Hello!')
}
}
}
</script>
With shorthand:
htmlCopyEdit<button @click="greet">Greet</button>
Passing Arguments to Methods
You can pass arguments to methods directly in the template:
htmlCopyEdit<template>
<button @click="greet('John')">Greet John</button>
</template>
<script>
export default {
methods: {
greet(name) {
alert(`Hello, ${name}!`)
}
}
}
</script>

Accessing Event Object


Sometimes, you need to access the native event object. Vue automatically passes
the event object as the last argument. You can explicitly access it using $event:
htmlCopyEdit<template>
<input @keyup.enter="logMessage($event)">
</template>
<script>
export default {
methods: {
logMessage(event) {
console.log(event.target.value)
}
}
}
</script>
⚡ 3. Event Modifiers
Vue provides event modifiers to make event handling simpler and cleaner.
Common Modifiers
• .prevent — Prevents the default behavior.
• .stop — Stops event propagation.
• .self — Triggers the handler only if the event target is the element itself.
• .once — Ensures the event handler is executed only once.
• .capture — Adds the event listener in capture mode.
Example:
htmlCopyEdit<template>
<form @submit.prevent="submitForm">
<input type="text" v-model="name">
<button type="submit">Submit</button>
</form>
</template>
<script>
export default {
data() {
return {
name: ''
}
},
methods: {
submitForm() {
alert(`Submitted: ${this.name}`)
}
}
}
</script>
In this example:
• @submit.prevent prevents the form from reloading the page when
submitted.
🔥 4. Key Modifiers
Key modifiers simplify keyboard event handling:
• .enter — Triggers on Enter key.
• .esc — Triggers on Escape key.
• .tab — Triggers on Tab key.
• .delete — Triggers on Delete (Backspace included).
• .arrow-up, .arrow-down, .arrow-left, .arrow-right — Arrow keys.
Example:
htmlCopyEdit<template>
<input @keyup.enter="submit" placeholder="Press Enter">
</template>
<script>
export default {
methods: {
submit() {
alert('Submitted!')
}
}
}
</script>

🏆 5. Mouse Event Modifiers


Mouse modifiers make handling mouse events easier:
• .left — Triggers only on left click.
• .right — Triggers only on right click.
• .middle — Triggers only on middle click.
• .exact — Ensures no extra modifiers (like Ctrl or Shift) are pressed.
Example:
htmlCopyEdit<template>
<button @click.right.prevent="rightClick">Right Click Me</button>
</template>
<script>
export default {
methods: {
rightClick() {
alert('Right click detected!')
}
}
}
</script>

⚙️ 6. Inline Handlers vs Methods


You can write handlers inline if they’re simple:
htmlCopyEdit<button @click="count++">Increment</button>
However, if the logic is complex, use methods:
htmlCopyEdit<template>
<button @click="incrementCount">Increment</button>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
incrementCount() {
this.count++
}
}
}
</script>
📊 7. Using Methods in Computed Properties
While methods are used for actions, computed properties are used to derive
values from existing data. If you only need a calculated value without triggering
side effects, use computed properties.
Example:
htmlCopyEdit<template>
<p>Full Name: {{ fullName }}</p>
</template>
<script>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
}
</script>

🎉 8. Methods vs Computed vs Watch


Feature Use Case Example
Methods Perform actions (e.g., increment counter). incrementCount()
Computed Derive new data from existing state. fullName()
Watch React to changes in data. watch: { count(newVal) }
6. Computed Properties and Watchers in Vue.js
In Vue, computed properties and watchers are powerful features that help you
handle complex data manipulation and respond to changes in state. Let’s break
them down in detail, understand their use cases, and see practical examples.

1. Computed Properties
Computed properties are used to derive new values based on existing data. They
are reactive, meaning they automatically update when the underlying data
changes. Computed properties are particularly useful for handling logic that
would otherwise clutter your templates.
Why Use Computed Properties?
1. Avoid redundancy — Helps prevent repeating logic in templates.
2. Improved readability — Keeps your templates clean and focused.
3. Caching — Computed properties are cached based on their dependencies,
meaning they won’t recalculate unless the dependent data changes.

Basic Example
htmlCopyEdit<template>
<div>
<p>First Name: {{ firstName }}</p>
<p>Last Name: {{ lastName }}</p>
<p>Full Name: {{ fullName }}</p>
</div>
</template>
<script>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
}
</script>
In this example:
• fullName is a computed property that combines firstName and lastName.
• If either firstName or lastName changes, fullName is automatically
updated.

Computed vs Methods
You might wonder, "Why not just use a method?"
htmlCopyEdit<template>
<p>Full Name: {{ getFullName() }}</p>
</template>
<script>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
methods: {
getFullName() {
return this.firstName + ' ' + this.lastName
}
}
}
</script>
The difference is that computed properties are cached, whereas methods are
called every time the template is rendered. If you access a computed property
multiple times in the template, it calculates once and reuses the result, improving
performance.

Getters and Setters


Computed properties can have both getters and setters. This is useful when you
want to compute a value and also modify the original data.
htmlCopyEdit<template>
<input v-model="fullName" />
<p>First Name: {{ firstName }}</p>
<p>Last Name: {{ lastName }}</p>
</template>
<script>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName: {
get() {
return this.firstName + ' ' + this.lastName
},
set(value) {
const parts = value.split(' ')
this.firstName = parts[0]
this.lastName = parts[1] || ''
}
}
}
}
</script>
In this example:
• The get function returns the concatenated full name.
• The set function splits the input and updates firstName and lastName
accordingly.

2. Watchers
Watchers allow you to perform actions in response to changes in data. They’re
useful when you need to perform side effects like fetching data, updating local
storage, or triggering custom logic when data changes.
Why Use Watchers?
1. React to data changes.
2. Perform asynchronous operations like API calls.
3. Watch deeply nested objects or arrays for changes.

Basic Example
htmlCopyEdit<template>
<input v-model="name" placeholder="Enter your name">
</template>
<script>
export default {
data() {
return {
name: ''
}
},
watch: {
name(newValue, oldValue) {
console.log(`Name changed from "${oldValue}" to "${newValue}"`)
}
}
}
</script>
In this example:
• The name watcher triggers every time name changes, logging the new and
old values.

Watching Multiple Data Sources


You can watch multiple properties by defining multiple watchers.
htmlCopyEdit<template>
<input v-model="firstName" placeholder="First Name">
<input v-model="lastName" placeholder="Last Name">
</template>
<script>
export default {
data() {
return {
firstName: '',
lastName: ''
}
},
watch: {
firstName(newValue) {
console.log(`First Name changed to: ${newValue}`)
},
lastName(newValue) {
console.log(`Last Name changed to: ${newValue}`)
}
}
}
</script>

Deep Watching
By default, watchers only track top-level changes. If you want to watch nested
objects or arrays, you can add the deep: true option.
htmlCopyEdit<template>
<input v-model="user.name">
</template>
<script>
export default {
data() {
return {
user: {
name: ''
}
}
},
watch: {
user: {
handler(newValue) {
console.log('User changed:', newValue)
},
deep: true
}
}
}
</script>

Immediate Execution
By default, watchers only run when the watched data changes. If you want a
watcher to execute immediately upon creation, use the immediate: true option.
htmlCopyEdit<script>
export default {
data() {
return {
count: 0
}
},
watch: {
count: {
handler(newValue) {
console.log(`Count is now: ${newValue}`)
},
immediate: true
}
}
}
</script>
In this example:
• The watcher runs immediately after the component is created, logging the
initial count.

3. Computed vs Watchers
Feature Use Case Example
Computed Use when deriving data from existing fullName()
state.
Watcher Use when reacting to changes or watch: { count(newVal) {
triggering side effects. ... } }

4. Common Use Cases


• Use computed properties for:
o Displaying formatted data.
o Calculating totals, averages, or combined strings.
• Use watchers for:
o Fetching data from APIs when a search term changes.
o Performing expensive operations (e.g., debounce input).
Example: Fetch data when searchTerm changes.
htmlCopyEdit<template>
<input v-model="searchTerm" placeholder="Search">
</template>
<script>
export default {
data() {
return {
searchTerm: ''
}
},
watch: {
searchTerm(newTerm) {
this.fetchData(newTerm)
}
},
methods: {
fetchData(query) {
console.log('Fetching data for:', query)
}
}
}
</script>

7. Conditional Rendering in Vue.js


Conditional rendering is a core concept in Vue.js that allows you to dynamically
show or hide elements in the DOM based on conditions. Vue provides directives
like v-if, v-else, v-else-if, and v-show to handle these scenarios. Let’s dive deep
into each directive, understand their use cases, and see practical examples.

1. v-if — Conditional Rendering


The v-if directive renders an element only if the condition is true. If the condition
is false, the element is not added to the DOM at all.
Syntax
htmlCopyEdit<div v-if="condition">
This will only appear if "condition" is true.
</div>
Example
htmlCopyEdit<template>
<button @click="toggle">Toggle Message</button>
<p v-if="showMessage">Hello, Vue.js!</p>
</template>
<script>
export default {
data() {
return {
showMessage: false
}
},
methods: {
toggle() {
this.showMessage = !this.showMessage
}
}
}
</script>
In this example:
• When showMessage is true, the <p> tag is rendered.
• When showMessage is false, the <p> tag is removed from the DOM.

2. v-else — Fallback Content


The v-else directive renders content only when the preceding v-if is false. It must
immediately follow a v-if or v-else-if block.
Example
htmlCopyEdit<template>
<button @click="toggle">Toggle</button>
<p v-if="isLoggedIn">Welcome, User!</p>
<p v-else>Please log in.</p>
</template>
<script>
export default {
data() {
return {
isLoggedIn: false
}
},
methods: {
toggle() {
this.isLoggedIn = !this.isLoggedIn
}
}
}
</script>
In this example:
• v-if shows a welcome message when isLoggedIn is true.
• v-else shows a login prompt when isLoggedIn is false.

3. v-else-if — Multiple Conditions


The v-else-if directive adds additional conditions when v-if is false. You can chain
multiple v-else-if blocks.
Example
htmlCopyEdit<template>
<button @click="changeStatus">Change Status</button>
<p v-if="status === 'success'">Success!</p>
<p v-else-if="status === 'error'">Error occurred.</p>
<p v-else-if="status === 'loading'">Loading...</p>
<p v-else>Unknown status.</p>
</template>
<script>
export default {
data() {
return {
status: 'loading'
}
},
methods: {
changeStatus() {
const statuses = ['success', 'error', 'loading', 'unknown']
this.status = statuses[Math.floor(Math.random() * statuses.length)]
}
}
}
</script>
In this example:
• Different messages are displayed based on the value of status.
• v-else handles cases where none of the conditions match.

4. v-show — Toggle Visibility


The v-show directive shows or hides an element by toggling its CSS display
property. Unlike v-if, the element is always rendered in the DOM.
Syntax
htmlCopyEdit<div v-show="condition">
This element is always in the DOM, but its visibility is controlled by CSS.
</div>
Example
htmlCopyEdit<template>
<button @click="toggle">Toggle Message</button>
<p v-show="showMessage">Hello, Vue.js!</p>
</template>
<script>
export default {
data() {
return {
showMessage: false
}
},
methods: {
toggle() {
this.showMessage = !this.showMessage
}
}
}
</script>
In this example:
• The <p> tag is always in the DOM, but its visibility is controlled using
display: none when showMessage is false.

5. v-if vs v-show — Key Differences


Feature v-if v-show
Rendering Adds/removes element from Toggles the element’s display
the DOM. property.
Performance Best for conditions that rarely Best for conditions that change
change. frequently.
Initial Load Higher cost (adds/removes Lower cost (only toggles
DOM nodes). visibility).
Use Cases Form submissions, API loading Tabs, modals, tooltips.
states.

6. Conditional Rendering with Template


You can wrap multiple elements under a <template> tag when using v-if or v-
show. The <template> itself doesn’t get rendered into the DOM.
Example
htmlCopyEdit<template>
<button @click="toggle">Toggle Items</button>
<template v-if="showItems">
<p>Item 1</p>
<p>Item 2</p>
<p>Item 3</p>
</template>
</template>
<script>
export default {
data() {
return {
showItems: false
}
},
methods: {
toggle() {
this.showItems = !this.showItems
}
}
}
</script>
In this example:
• v-if is applied to the <template>, controlling the visibility of all child
elements.

7. Handling Conditional Classes and Styles


You can dynamically apply classes and styles using v-bind and conditional
expressions.
Example: Dynamic Classes
htmlCopyEdit<template>
<button @click="toggleActive" :class="{ active: isActive }">
Click Me
</button>
</template>
<script>
export default {
data() {
return {
isActive: false
}
},
methods: {
toggleActive() {
this.isActive = !this.isActive
}
}
}
</script>
<style>
.active {
background-color: green;
color: white;
}
</style>
Example: Dynamic Styles
htmlCopyEdit<template>
<p :style="{ color: isRed ? 'red' : 'black' }">
Dynamic Text Color
</p>
<button @click="toggleColor">Toggle Color</button>
</template>
<script>
export default {
data() {
return {
isRed: false
}
},
methods: {
toggleColor() {
this.isRed = !this.isRed
}
}
}
</script>
8. List Rendering with v-for in Vue.js
In Vue.js, the v-for directive is used to render lists of items based on an array or
object. Whether you’re rendering a simple list, iterating over an object’s
properties, or rendering nested data, v-for makes it easy and intuitive. Let’s break
this down step by step.

🟢 1. Basic Syntax
The basic syntax for v-for is:
htmlCopyEdit<li v-for="item in items" :key="item.id">
{{ item }}
</li>
• item in items: Loops through the items array and binds each element to
item.
• :key="item.id": Helps Vue efficiently track and update items when the list
changes.

🟢 2. Rendering Arrays
Let’s start with a simple example:
htmlCopyEdit<template>
<ul>
<li v-for="fruit in fruits" :key="fruit">
{{ fruit }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
fruits: ['Apple', 'Banana', 'Orange', 'Grapes']
}
}
}
</script>
In this example:
• v-for iterates over the fruits array, rendering each fruit as a list item.
• The :key is set to fruit for uniqueness.

🟢 3. Index in v-for
Sometimes you may need the index of each item:
htmlCopyEdit<template>
<ul>
<li v-for="(fruit, index) in fruits" :key="index">
{{ index + 1 }}. {{ fruit }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
fruits: ['Apple', 'Banana', 'Orange', 'Grapes']
}
}
}
</script>
In this example:
• index gives you the current iteration index (starting from 0).
• index + 1 makes the list numbering start from 1.

🟢 4. Rendering Objects
You can iterate over the properties of an object:
htmlCopyEdit<template>
<ul>
<li v-for="(value, key) in person" :key="key">
{{ key }}: {{ value }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
person: {
name: 'John Doe',
age: 30,
city: 'New York'
}
}
}
}
</script>
In this example:
• key is the object’s property name.
• value is the value of each property.

🟢 5. Using v-for with a Range


You can generate lists with numbers using a range:
htmlCopyEdit<template>
<ul>
<li v-for="n in 5" :key="n">
Item {{ n }}
</li>
</ul>
</template>
In this example:
• v-for="n in 5" generates numbers from 1 to 5.

🟢 6. Key Attribute
The :key attribute is crucial for performance. It helps Vue track changes when
adding, removing, or reordering items.
htmlCopyEdit<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
Why use :key?
• It gives each item a unique identity.
• Without :key, Vue relies on default behavior, which can cause unexpected
results when the list changes.

🟢 7. Nested Loops
You can nest multiple v-for loops:
htmlCopyEdit<template>
<div v-for="(group, index) in groups" :key="index">
<h3>Group {{ index + 1 }}</h3>
<ul>
<li v-for="(item, i) in group.items" :key="i">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
groups: [
{ items: ['Apple', 'Banana'] },
{ items: ['Carrot', 'Broccoli'] }
]
}
}
}
</script>
In this example:
• The outer loop iterates over groups.
• The inner loop iterates over each group’s items.

🟢 8. Adding/Removing Items
You can dynamically add or remove items:
htmlCopyEdit<template>
<div>
<input v-model="newItem" placeholder="Add item" @keyup.enter="addItem"
/>
<button @click="addItem">Add</button>
<ul>
<li v-for="(item, index) in items" :key="index">
{{ item }} <button @click="removeItem(index)">Remove</button>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
items: ['Item 1', 'Item 2'],
newItem: ''
}
},
methods: {
addItem() {
if (this.newItem.trim()) {
this.items.push(this.newItem)
this.newItem = ''
}
},
removeItem(index) {
this.items.splice(index, 1)
}
}
}
</script>

🟢 9. v-for with Computed Properties


If you want to filter or modify the list, use computed properties:
htmlCopyEdit<template>
<ul>
<li v-for="item in filteredItems" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, name: 'Apple', type: 'fruit' },
{ id: 2, name: 'Carrot', type: 'vegetable' },
{ id: 3, name: 'Banana', type: 'fruit' }
]
}
},
computed: {
filteredItems() {
return this.items.filter(item => item.type === 'fruit')
}
}
}
</script>
In this example:
• filteredItems is a computed property that returns only fruits.

🟢 10. v-for with Template


You can wrap multiple elements under a <template> to avoid unnecessary
markup:
htmlCopyEdit<template>
<template v-for="item in items" :key="item.id">
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
</template>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, name: 'Apple', description: 'A red fruit' },
{ id: 2, name: 'Carrot', description: 'A root vegetable' }
]
}
}
}
</script>
The <template> tag itself doesn’t render anything, but its children do.

State Management with Pinia or Vuex


In larger applications, managing shared state between components can become
complex. State management libraries like Pinia and Vuex help centralize and
manage the state, making the code more organized, predictable, and easier to
debug.
1. What is State Management?
State management is the practice of handling the state (data) of your application
in a predictable way. In a simple Vue app, the state can be stored in the
component’s data() or passed around using props and emit for parent-child
communication. However, as your app grows, managing state across multiple
components becomes difficult.
A state management library provides a single source of truth by centralizing the
state into one global store, making it accessible across components.

2. Why Use a State Management Library?


Here are some reasons to use a state management library:
• Centralized State: Keeps all shared data in one place.
• Predictability: Follows strict rules for updating the state.
• Reactivity: Automatically reflects state changes in the UI.
• Debugging: Provides tools for tracking state changes over time.
• Better Code Structure: Reduces prop drilling and simplifies data flow.

3. Pinia vs Vuex
Vuex was the default state management library for Vue 2 and early Vue 3
projects. However, Pinia has become the new standard in Vue 3 due to its
simplicity and integration with the Composition API. Let’s compare them:

Feature Pinia Vuex


API Design Simple and intuitive Complex, boilerplate-heavy
TypeScript Support Built-in Requires extra setup
Composition API Natively supported Requires extra effort
Devtools Support Full integration Full integration
Mutations Not required Mandatory for state changes
Learning Curve Easier Steeper
Pinia simplifies state management while keeping the same benefits as Vuex,
making it the recommended choice for modern Vue 3 projects.

4. Setting Up Pinia
Let’s set up Pinia in a Vue 3 project:
Installation:
bashCopyEditnpm install pinia
Main Entry (main.js or main.ts):
javascriptCopyEditimport { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')

5. Creating a Pinia Store


You define a store in a separate file, like stores/counter.js:
javascriptCopyEditimport { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
}
},
getters: {
doubleCount: (state) => state.count * 2
}
})
• state: Holds the reactive data.
• actions: Functions that modify the state.
• getters: Computed properties for deriving data from the state.

6. Using the Store in a Component


htmlCopyEdit<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double Count: {{ counter.doubleCount }}</p>
<button @click="counter.increment">Increment</button>
</div>
</template>
<script>
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const counter = useCounterStore()
return { counter }
}
}
</script>
• useCounterStore() gives you access to the store.
• The store is reactive, so UI updates automatically when state changes.

7. Vuex Overview
If you’re using Vuex, the setup is slightly different:
Installation:
bashCopyEditnpm install vuex
Store Setup (store/index.js):
javascriptCopyEditimport { createStore } from 'vuex'
export default createStore({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
increment({ commit }) {
commit('increment')
}
},
getters: {
doubleCount: (state) => state.count * 2
}
})
Main Entry (main.js):
javascriptCopyEditimport { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')
Using the Vuex Store in a Component:
htmlCopyEdit<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="$store.dispatch('increment')">Increment</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['doubleCount'])
}
}
</script>
Vuex requires more boilerplate, especially with mutations and actions.

8. When to Use Pinia vs Vuex


• Pinia: Use it for new Vue 3 projects. It integrates seamlessly with the
Composition API and is much simpler to set up.
• Vuex: Use it if you’re working on a legacy project with Vue 2 or if your team
is already experienced with Vuex.

Composition API vs Options API in Vue.js


In Vue.js, the Composition API and Options API are two approaches to building
components. The Options API has been the traditional way of structuring
components in Vue 2, while the Composition API was introduced in Vue 3 to
provide better flexibility and code organization, especially for complex
components. Let’s dive into these two approaches, compare them, and
understand when to use each one.

1. Understanding the Options API


The Options API organizes component logic into distinct options like data,
methods, computed, watch, and lifecycle hooks. This separation makes it simple
to understand but can lead to scattered logic in complex components.
Example:
htmlCopyEdit<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
In this example:
• data() returns the component’s state.
• methods contains functions that interact with the state.

2. Understanding the Composition API


The Composition API provides a more flexible and organized way to manage
component logic by grouping related logic into reusable functions (composables).
It’s especially beneficial for large applications and cleaner code separation.
Example:
htmlCopyEdit<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
}
}
</script>
In this example:
• ref() creates reactive data.
• setup() is the entry point for Composition API logic.
• The count variable is accessed using count.value.

3. Comparing Composition API and Options API


Feature Options API Composition API
Structure Organizes logic into distinct Centralizes logic in setup() for
sections (data, methods, better organization.
computed).
Readability Clear for simple components but Improves readability in
gets cluttered in complex ones. complex components.
Reusability Hard to reuse logic across Encourages composables,
components. making logic highly reusable.
TypeScript Requires workarounds. Designed with TypeScript in
Support mind.
Code Splits logic across multiple Keeps related logic close
Organization options. together.
Learning Curve Easier for beginners. Requires understanding ref,
reactive, computed, etc.

4. When to Use Composition API


• For complex components with multiple features.
• When you need to reuse logic across multiple components.
• In projects using TypeScript.
• For better testability and code organization.
5. When to Use Options API
• For simple components and smaller projects.
• When onboarding developers familiar with the Options API.
• If maintaining legacy code.

6. Mixing Composition API with Options API


Vue 3 allows using the Composition API inside the Options API through setup():
javascriptCopyEditexport default {
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
}

Reusability with Composables in Vue.js


In complex applications, you often need to reuse logic across multiple
components. The Composition API introduces composables — reusable functions
that encapsulate logic and can be shared across different parts of your app. Let’s
dive deep into composables, understand how they work, and explore best
practices.

1. What Are Composables?


Composables are functions that use the Composition API’s features (ref, reactive,
computed, watch, etc.) and return reusable logic. Instead of duplicating code
across components, you can extract shared logic into composables, making your
code cleaner, more organized, and easier to maintain.
A composable:
• Is just a function.
• Can encapsulate state, computed properties, and methods.
• Promotes separation of concerns by grouping related logic together.

2. Why Use Composables?


• Reusability: Extract common logic and reuse it in different components.
• Separation of Concerns: Keeps your components lean and focused on
rendering.
• Testability: Composables are pure functions, making them easy to test.
• Maintainability: Centralizes logic, making changes easier to manage.

3. Creating a Composable
Let’s create a simple composable to handle a counter.
Step 1: Create a Composable
Create a new file useCounter.js inside a composables or composables folder:
javascriptCopyEdit// composables/useCounter.js
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => (count.value = 0)
return {
count,
increment,
decrement,
reset
}
}
• count is a reactive state.
• increment, decrement, and reset handle logic.
4. Using the Composable in a Component
Now, use this composable inside a Vue component:
htmlCopyEdit<template>
<div>
<h1>Count: {{ count }}</h1>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="reset">Reset</button>
</div>
</template>
<script>
import { useCounter } from '@/composables/useCounter'
export default {
setup() {
const { count, increment, decrement, reset } = useCounter()
return { count, increment, decrement, reset }
}
}
</script>
• useCounter() returns the count state and the functions, which are used
directly in the template.

5. More Complex Composables


Let’s create a composable for fetching data from an API.
Step 1: Create a Composable
javascriptCopyEdit// composables/useFetch.js
import { ref, onMounted } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(true)
const fetchData = async () => {
loading.value = true
try {
const response = await fetch(url)
if (!response.ok) throw new Error('Failed to fetch data')
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
onMounted(fetchData)
return { data, error, loading, fetchData }
}
Step 2: Use the Composable in a Component
htmlCopyEdit<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<div v-else>
<pre>{{ data }}</pre>
<button @click="fetchData">Reload</button>
</div>
</template>
<script>
import { useFetch } from '@/composables/useFetch'
export default {
setup() {
const { data, error, loading, fetchData } =
useFetch('https://api.example.com/items')
return { data, error, loading, fetchData }
}
}
</script>
• useFetch handles API requests and exposes data, error, and loading.
• It fetches data automatically when the component mounts.
6. Best Practices for Composables
1. Naming Conventions: Use the use prefix (e.g., useCounter, useFetch).
2. Encapsulate Logic: Group related logic into a single composable.
3. Return Only What’s Needed: Expose only the necessary state and
functions.
4. Keep Composables Pure: Avoid side effects. Let the component handle side
effects like rendering.
5. Test Your Composables: Since composables are pure functions, they’re
easy to test.

7. When to Use Composables


• When multiple components share similar logic.
• When separating business logic from UI logic.
• When simplifying complex components by offloading logic to composables.

8. Composition API Without Composables


Without composables, the logic gets repeated across components. For example:
javascriptCopyEditexport default {
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
}
If you reuse this logic in multiple components, it results in code duplication.
Composables eliminate this redundancy.

9. Combining Composables
You can compose composables by using one inside another. For instance:
javascriptCopyEdit// composables/useAuth.js
import { ref } from 'vue'
export function useAuth() {
const isAuthenticated = ref(false)
const login = () => (isAuthenticated.value = true)
const logout = () => (isAuthenticated.value = false)
return { isAuthenticated, login, logout }
}
// composables/useUser.js
import { useAuth } from './useAuth'
export function useUser() {
const { isAuthenticated, login, logout } = useAuth()
const user = ref(null)
const fetchUser = () => {
if (isAuthenticated.value) {
user.value = { name: 'John Doe', email: '[email protected]' }
}
}
return { user, login, logout, fetchUser }
}
This composability makes logic highly modular and reusable.

Teleport and Portals in Vue.js


In Vue.js, Teleport allows you to render a part of your component’s template in a
different part of the DOM. This is particularly useful for modals, popups,
notifications, and tooltips that need to be rendered outside the parent
component’s hierarchy but still maintain reactivity. Let’s dive deep into this
concept, understand when to use it, and see practical examples.

1. What is Teleport?
In a typical Vue app, each component’s template is rendered inside its parent’s
DOM tree. But sometimes, you need to "teleport" some elements to a different
location in the DOM.
For example:
• Modals should appear on top of everything, outside the normal component
structure.
• Dropdowns and tooltips may need to escape overflow: hidden containers.
• Notifications or alerts may need to appear at the root of the app.
Teleport solves these issues by letting you render content into a different part of
the DOM, while still being controlled by the parent component.

2. How Does Teleport Work?


Vue provides a built-in <Teleport> component, which takes a to prop specifying
the CSS selector of the target container where you want to teleport content.
Basic Syntax:
htmlCopyEdit<Teleport to="body">
<div class="modal">
<h2>Modal Title</h2>
<p>This modal is teleported to the body!</p>
</div>
</Teleport>
In this example:
• Even though the <Teleport> is inside another component, its content (<div
class="modal">) gets moved directly under the <body> tag.
• This makes sure the modal isn’t constrained by parent styles like overflow:
hidden or z-index.

3. When to Use Teleport


• Modals and Dialogs: Ensure they overlay everything.
• Notifications/Toasts: Render alerts at the top level of the app.
• Dropdowns/Tooltips: Avoid being clipped by parent containers.
• Portals/Popups: Separate UI concerns while maintaining reactivity.

4. Example: Modal with Teleport


Let’s build a simple modal that uses teleport.
Step 1: Create the Modal Component
htmlCopyEdit<!-- Modal.vue -->
<template>
<Teleport to="body">
<div v-if="isOpen" class="modal-overlay" @click.self="close">
<div class="modal">
<h2>Modal Title</h2>
<p>This is a teleported modal!</p>
<button @click="close">Close</button>
</div>
</div>
</Teleport>
</template>
<script>
export default {
props: {
isOpen: Boolean
},
emits: ['close'],
methods: {
close() {
this.$emit('close')
}
}
}
</script>
<style>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal {
background: white;
padding: 20px;
border-radius: 8px;
min-width: 300px;
}
</style>

Step 2: Use the Modal in a Parent Component


htmlCopyEdit<template>
<div>
<h1>Teleport Example</h1>
<button @click="showModal = true">Open Modal</button>
<Modal :isOpen="showModal" @close="showModal = false" />
</div>
</template>
<script>
import Modal from './Modal.vue'
import { ref } from 'vue'
export default {
components: { Modal },
setup() {
const showModal = ref(false)
return { showModal }
}
}
</script>
Now, when you click "Open Modal," the modal content gets teleported to body
while keeping the logic inside the parent component.

5. Targeting Specific Containers


You’re not limited to body — you can teleport anywhere in the DOM:
htmlCopyEdit<Teleport to="#modal-container">
<div class="modal">This modal goes inside #modal-container!</div>
</Teleport>
Just ensure the target container exists in your HTML:
htmlCopyEdit<body>
<div id="app"></div>
<div id="modal-container"></div>
</body>

6. Fallback Behavior
If the target container doesn’t exist, Teleport renders the content in its original
location. This ensures that your app doesn’t break when the target isn’t found.
htmlCopyEdit<Teleport to="#non-existent-container">
<p>This renders in place if the target is missing.</p>
</Teleport>

7. Best Practices
1. Keep Logic Close to UI: Keep modal logic close to the components that
trigger them.
2. Avoid Overusing Teleport: Use teleport only when absolutely necessary to
avoid breaking the app structure.
3. Test Edge Cases: Handle scenarios where the target container may not
exist.
4. Combine with State Management: Use Pinia or Vuex to control modal
visibility across multiple components.

8. Teleport vs Portals
If you’re coming from other frameworks like React, Teleport is equivalent to
Portals. The concept is the same — rendering content outside the normal
component tree while maintaining the same reactivity and event handling.
In React:
javascriptCopyEditReactDOM.createPortal(<Modal />, document.body)
In Vue:
htmlCopyEdit<Teleport to="body">
<Modal />
</Teleport>

9. When Not to Use Teleport


• When the content doesn’t need to break out of the component tree.
• When simple conditionals (v-if or v-show) suffice.
• For small UI pieces that don’t affect layout, like buttons or labels.

Suspense and Lazy Loading in Vue.js


Vue 3 introduced two powerful features: Suspense and Lazy Loading. These tools
help manage asynchronous components and improve performance by loading
parts of your app only when needed. Let’s dive deep into these concepts, explore
practical use cases, and understand how to implement them effectively.

1. What is Suspense in Vue.js?


Suspense is a special built-in component in Vue 3 that allows you to handle
asynchronous components gracefully. It renders fallback content while waiting for
async operations, such as data fetching or lazy-loaded components, to complete.
Key Points:
• Suspense works with async components.
• You can define a fallback slot to show loading indicators or placeholders.
• The component only renders once all async dependencies are resolved.

2. Basic Syntax of Suspense


Here’s the basic structure of a <Suspense> component:
htmlCopyEdit<Suspense>
<!-- Main content when resolved -->
<template #default>
<AsyncComponent />
</template>
<!-- Fallback content while loading -->
<template #fallback>
<p>Loading...</p>
</template>
</Suspense>
In this example:
• The default slot renders the AsyncComponent once it’s ready.
• The fallback slot shows a loading message while waiting.

3. Creating an Async Component


You can make any component async by returning a Promise inside the setup()
function:
htmlCopyEdit<!-- AsyncComponent.vue -->
<template>
<div>
<h1>Data Loaded: {{ data }}</h1>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
async setup() {
const data = ref(null)
// Simulating API Call
await new Promise(resolve => setTimeout(resolve, 2000))
data.value = 'Hello from Async Component!'
return { data }
}
}
</script>

4. Using Suspense with Async Components


htmlCopyEdit<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<p>Loading data...</p>
</template>
</Suspense>
</template>
<script>
import AsyncComponent from './AsyncComponent.vue'
export default {
components: { AsyncComponent }
}
</script>
• When the component is loading, Loading data... is shown.
• After 2 seconds, the actual content of AsyncComponent appears.

5. What is Lazy Loading?


Lazy Loading is a technique to load parts of your app only when they’re needed.
This reduces initial bundle size and improves performance, especially for large
apps.
Vue 3 supports lazy loading of components using dynamic imports with
defineAsyncComponent.

6. Lazy Loading Syntax


To lazy-load a component, use defineAsyncComponent:
javascriptCopyEditimport { defineAsyncComponent } from 'vue'
// Lazy load component
const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
)
Now, this component will only load when rendered for the first time.

7. Combining Suspense with Lazy Loading


Suspense works beautifully with lazy-loaded components, ensuring fallback
content is displayed while waiting for the component to load.
htmlCopyEdit<template>
<Suspense>
<template #default>
<LazyComponent />
</template>
<template #fallback>
<p>Loading component...</p>
</template>
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue'
// Lazy loading
const LazyComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
)
export default {
components: { LazyComponent }
}
</script>
• The component is loaded only when it’s rendered.
• Suspense ensures a smooth experience with a loading placeholder.
8. Error Handling with Lazy Loading
You can handle errors and timeouts with defineAsyncComponent:
javascriptCopyEditimport { defineAsyncComponent } from 'vue'
const LazyComponent = defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
delay: 200, // Wait 200ms before showing the loading state
timeout: 3000, // Show error if loading takes longer than 3s
errorComponent: () => import('./ErrorComponent.vue')
})
This way:
• A loading indicator shows after delay.
• If the component doesn’t load within timeout, ErrorComponent is
displayed.

9. Real-World Use Cases


• Route-based Lazy Loading: Load routes only when users visit them.
• Heavy Components: Lazy load charts, maps, and other large components.
• Third-party Libraries: Only import libraries when required (e.g., modals,
image galleries).

10. Lazy Loading Routes with Vue Router


Vue Router supports lazy loading with dynamic imports:
javascriptCopyEditimport { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
component: () => import('./components/Home.vue')
},
{
path: '/about',
component: () => import('./components/About.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
• Each route is only loaded when the user navigates to it.
• Reduces initial bundle size.

11. When to Use Suspense and Lazy Loading


Use Suspense when:
• You’re working with async components.
• You want a fallback UI while waiting for data.
• You need to handle multiple async dependencies cleanly.
Use Lazy Loading when:
• You want to reduce initial bundle size.
• Certain components are rarely used (e.g., modals, charts).
• Routes or pages can be loaded dynamically.

12. Limitations of Suspense


• Suspense only works with components — not with regular functions or
promises.
• It only supports one async dependency at a time per component.
• If multiple components need to load async data, wrap each one in its own
<Suspense>.

Error Handling and Fallback UI in Vue.js


Error handling is crucial in any web application to ensure a smooth user
experience, even when things go wrong. Vue.js provides several mechanisms to
handle errors gracefully and display fallback UI when necessary. In this deep dive,
we’ll cover:
1. Global Error Handling
2. Error Boundaries
3. Handling Asynchronous Errors
4. Using Suspense for Error Handling
5. Creating Custom Error Components
6. Best Practices
Let’s get started!

1. Global Error Handling


Vue provides a global error handler that catches errors anywhere in the app,
making it a good place to handle unexpected failures or log errors.
Setting Up Global Error Handling:
You can set up a global error handler in the main.js (or main.ts for TypeScript) file:
javascriptCopyEditconst app = createApp(App)
// Global Error Handler
app.config.errorHandler = (err, instance, info) => {
console.error(`Error: ${err.message}\nInfo: ${info}`)
// Optionally send the error to a logging service
}
app.mount('#app')
• err: The error object.
• instance: The component instance where the error occurred.
• info: Provides additional context, like "render function" or "lifecycle hook".
Example Usage:
htmlCopyEdit<template>
<div>{{ 1 / 0 }}</div> <!-- This throws an error -->
</template>
The global handler catches the error and logs it.
2. Error Boundaries
Error boundaries let you catch errors in specific parts of the component tree and
show fallback UI instead.
Creating an Error Boundary:
htmlCopyEdit<template>
<div>
<h1>Error Boundary Example</h1>
<ErrorBoundary>
<ProblematicComponent />
</ErrorBoundary>
</div>
</template>
<script>
import ErrorBoundary from './ErrorBoundary.vue'
import ProblematicComponent from './ProblematicComponent.vue'
export default {
components: { ErrorBoundary, ProblematicComponent }
}
</script>
ErrorBoundary.vue:
htmlCopyEdit<template>
<div v-if="hasError">
<p>Something went wrong. Please try again.</p>
</div>
<div v-else>
<slot />
</div>
</template>
<script>
export default {
data() {
return { hasError: false }
},
errorCaptured(err, instance, info) {
console.error('Error captured:', err)
this.hasError = true
return false // Prevent error propagation
}
}
</script>
• errorCaptured catches errors in child components.
• Returning false prevents bubbling to parent components.
ProblematicComponent.vue:
htmlCopyEdit<template>
<div>{{ null.toUpperCase() }}</div> <!-- Triggers error -->
</template>
The ErrorBoundary will catch the error and display a fallback message.

3. Handling Asynchronous Errors


In setup() or onMounted(), you can use try...catch blocks for async errors.
htmlCopyEdit<template>
<div v-if="error">Error: {{ error }}</div>
<div v-else-if="loading">Loading...</div>
<div v-else>{{ data }}</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const data = ref(null)
const error = ref(null)
const loading = ref(true)
onMounted(async () => {
try {
const response = await fetch('https://api.example.com/data')
if (!response.ok) throw new Error('Failed to fetch')
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
})
return { data, error, loading }
}
}
</script>
• Shows a loading indicator while fetching.
• Displays the error message if the fetch fails.

4. Using Suspense for Error Handling


Vue’s Suspense can handle errors in async components.
AsyncComponent.vue:
htmlCopyEdit<template>
<div>{{ data }}</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
async setup() {
const data = ref(null)
await new Promise((resolve, reject) => setTimeout(reject, 2000, 'Failed to
load'))
data.value = 'Loaded!'
return { data }
}
}
</script>
Parent Component:
htmlCopyEdit<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<p>Loading...</p>
</template>
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
errorComponent: () => import('./ErrorComponent.vue'),
delay: 200,
timeout: 3000
})
export default {
components: { AsyncComponent }
}
</script>
• Fallback UI shows during loading.
• ErrorComponent shows if loading fails.

5. Creating Custom Error Components


You can create reusable error components to handle errors consistently.
ErrorComponent.vue:
htmlCopyEdit<template>
<div class="error">
<h1>Error Occurred</h1>
<p>{{ message }}</p>
<button @click="$emit('retry')">Retry</button>
</div>
</template>
<script>
export default {
props: {
message: { type: String, required: true }
}
}
</script>
Usage:
htmlCopyEdit<template>
<ErrorComponent v-if="error" :message="error" @retry="fetchData" />
<p v-else>{{ data }}</p>
</template>

1. Code Splitting and Lazy Loading


Code splitting breaks your app into smaller bundles, loading only what’s
necessary. Vue supports this with dynamic imports.
Before (Without Lazy Loading):
javascriptCopyEditimport HeavyComponent from './HeavyComponent.vue'
export default {
components: { HeavyComponent }
}
After (With Lazy Loading):
javascriptCopyEditimport { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
export default {
components: { HeavyComponent }
}
Route-Based Code Splitting
Vue Router makes lazy loading routes easy:
javascriptCopyEditconst routes = [
{
path: '/about',
component: () => import('./About.vue') // Lazy load this route
}
]
Why it helps:
• Reduces initial load time.
• Improves performance, especially for large apps.

2. Using Suspense for Asynchronous Operations


<Suspense> provides a fallback UI while waiting for async content to load. Perfect
for fetching data or lazy-loaded components.
Example:
htmlCopyEdit<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<p>Loading...</p>
</template>
</Suspense>
• Reduces perceived load time.
• Provides better UX by avoiding blank screens.

3. Memoization with Computed Properties


Avoid recalculating expensive operations unnecessarily by using computed
properties.
Inefficient:
javascriptCopyEditmethods: {
calculate() {
return this.items.filter(item => item.active)
}
}
Optimized with Computed:
javascriptCopyEditcomputed: {
activeItems() {
return this.items.filter(item => item.active)
}
}
Why it helps:
• Computed properties cache results.
• Reduces redundant calculations.

4. Optimizing v-for with Key


Vue needs a unique key when rendering lists to track changes efficiently.
Inefficient:
htmlCopyEdit<div v-for="item in items">{{ item.name }}</div>
Optimized:
htmlCopyEdit<div v-for="item in items" :key="item.id">{{ item.name }}</div>
Why it helps:
• Improves diffing and patching performance.
• Prevents unnecessary re-renders.

5. Debouncing and Throttling Events


Avoid firing events too frequently by using debounce and throttle techniques.
Before (Without Debouncing):
htmlCopyEdit<input @input="onInput" />
After (With Debouncing):
javascriptCopyEditimport { ref, watch } from 'vue'
import debounce from 'lodash/debounce'
export default {
setup() {
const searchQuery = ref('')
watch(searchQuery, debounce((newValue) => {
console.log('Search:', newValue)
}, 300))
return { searchQuery }
}
}
Why it helps:
• Reduces the number of function calls.
• Improves responsiveness, especially on large datasets.

6. Avoiding Unnecessary Watchers


Only watch reactive properties when necessary. Excessive watchers slow things
down.
Inefficient:
javascriptCopyEditwatch(() => this.items, (newItems) => {
console.log(newItems)
})
Optimized:
javascriptCopyEditcomputed: {
activeItems() {
return this.items.filter(item => item.active)
}
}
Why it helps:
• Reduces overhead from unnecessary watchers.
• Computed properties are more efficient for derived state.

7. Virtual Scrolling for Large Lists


If your app displays thousands of items, render only what’s visible with virtual
scrolling.
Install a library like vue-virtual-scroller:
bashCopyEditnpm install vue-virtual-scroller
Example:
htmlCopyEdit<template>
<RecycleScroller
:items="items"
:item-size="50"
v-slot="{ item }"
>
<div>{{ item.name }}</div>
</RecycleScroller>
</template>
<script>
import { RecycleScroller } from 'vue-virtual-scroller'
export default {
components: { RecycleScroller },
data() {
return {
items: Array.from({ length: 10000 }, (_, i) => ({ name: `Item ${i}` }))
}
}
}
</script>
Why it helps:
• Reduces DOM nodes.
• Improves performance for massive datasets.
8. Server-Side Rendering (SSR)
Use Nuxt.js or Vue SSR to render pages on the server and send HTML directly to
the client, improving load times and SEO.
Example with Nuxt.js:
bashCopyEditnpx nuxi init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev
Why it helps:
• Faster initial load.
• Better SEO.

9. Monitoring Performance
Use tools like Vue Devtools or browser performance profiles to identify
bottlenecks.
bashCopyEditnpm install --save-dev @vue/devtools
What to check:
• Component re-renders.
• Event listeners.
• Watchers.
1. What is SSR?
In a typical Vue app, the browser receives a blank HTML shell and then downloads
JavaScript to build the UI. This is called Client-Side Rendering (CSR).
In SSR, the server pre-renders the HTML and sends it directly to the browser,
allowing users to see content immediately while JavaScript takes over afterward.
2. Benefits of SSR
Faster Initial Load: Pre-rendered pages reduce the time to first paint.
Improved SEO: Search engines can crawl content directly from the HTML.
Better Social Media Sharing: Metadata is ready when shared.
Great for Content-Heavy Apps: Ideal for blogs, e-commerce, and portfolios.

3. Setting Up Nuxt.js
First, install Nuxt.js:
bashCopyEditnpx nuxi init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev
You’ll get a running server at http://localhost:3000 by default.

4. Directory Structure
Nuxt organizes your project neatly:
perlCopyEditmy-nuxt-app
├── pages/ # Vue components mapped to routes
├── components/ # Reusable UI components
├── layouts/ # App-wide layouts
├── middleware/ # Functions to run before rendering
├── store/ # Vuex (optional)
├── static/ # Static files like images
└── nuxt.config.js # Nuxt configuration

5. Creating Pages and Routes


Nuxt automatically generates routes for every component inside the pages/
folder.
pages/index.vue:
htmlCopyEdit<template>
<div>
<h1>Welcome to Nuxt.js</h1>
<NuxtLink to="/about">About</NuxtLink>
</div>
</template>
pages/about.vue:
htmlCopyEdit<template>
<div>
<h1>About Page</h1>
<NuxtLink to="/">Home</NuxtLink>
</div>
</template>

Now visiting / and /about works automatically!

6. Fetching Data with SSR


Nuxt provides special hooks for fetching data server-side:
pages/posts.vue:
htmlCopyEdit<template>
<div>
<h1>Posts</h1>
<ul>
<li v-for="post in posts" :key="post.id">
{{ post.title }}
</li>
</ul>
</div>
</template>
<script setup>
const { data: posts } = await
useFetch('https://jsonplaceholder.typicode.com/posts')
</script>
This ensures that:
• Data is fetched before rendering the page.
• The initial HTML sent to the browser contains the fetched content.

7. Middleware and Authentication


Use middleware to add logic before rendering pages, like authentication checks:
middleware/auth.js:
javascriptCopyEditexport default function ({ store, redirect }) {
if (!store.state.authenticated) {
return redirect('/login')
}
}
pages/profile.vue:
javascriptCopyEditexport default {
middleware: 'auth'
}

Now users get redirected if they’re not authenticated!

8. Dynamic Routes and SEO


Create dynamic pages by using underscores in filenames:
pages/posts/[id].vue:
htmlCopyEdit<template>
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.body }}</p>
</div>
</template>
<script setup>
const { id } = useRoute().params
const { data: post } = await
useFetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
</script>
Now /posts/1 dynamically loads post ID 1!
For SEO, add head() in your components:
javascriptCopyEditexport default {
head() {
return {
title: this.post.title,
meta: [
{ hid: 'description', name: 'description', content: this.post.body }
]
}
}
}

9. Performance Optimization in SSR


Lazy Load Components:
javascriptCopyEditconst HeavyComponent = defineAsyncComponent(() =>
import('~/components/HeavyComponent.vue')
)

Use nuxt generate for Static Sites:


bashCopyEditnpm run generate
This creates static HTML files for each route, combining the best of SSR and static
sites.

10. Deploying Nuxt.js Apps


Deploy your Nuxt app to platforms like Vercel, Netlify, or Heroku.
For production builds:
bashCopyEditnpm run build
npm run start
• Vercel and Netlify automatically detect Nuxt projects.
• For manual deployments, ensure you serve the app with Node.js.
1. Introduction to TypeScript in Vue
TypeScript adds static typing to JavaScript, making your code more predictable
and easier to refactor. Key benefits include:
• Type Safety: Catch errors at compile time.
• Better IDE Support: Autocomplete, type hints, and inline documentation.
• Improved Readability: Makes code more self-documenting.

2. Setting Up a Vue Project with TypeScript


You can create a Vue 3 project with TypeScript using Vite or Vue CLI.
Using Vite:
bashCopyEditnpm create vite@latest my-vue-ts-app
cd my-vue-ts-app
npm install
npm install --save-dev typescript vue-tsc
Add a tsconfig.json file:
jsonCopyEdit{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"jsx": "preserve",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
Now you’re ready to use TypeScript in your Vue app!

3. Type Annotations and Interfaces


TypeScript uses annotations to define types for variables, functions, and objects.
typescriptCopyEditlet message: string = 'Hello, Vue with TypeScript!'
interface User {
id: number
name: string
}
const user: User = { id: 1, name: 'John Doe' }

4. Using TypeScript in Components


You can write Vue components with the Composition API or Options API. Let’s
see both approaches:
Composition API with TypeScript
htmlCopyEdit<template>
<div>
<h1>{{ message }}</h1>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const message = ref<string>('Hello from Composition API!')
</script>
Options API with TypeScript
htmlCopyEdit<template>
<div>{{ message }}</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup() {
const message = ref<string>('Hello from Options API!')
return { message }
}
})
</script>

5. Props, Emits, and Events


You can strongly type props and emits in Vue components.
htmlCopyEdit<template>
<button @click="handleClick">Click Me</button>
</template>
<script lang="ts" setup>
defineProps<{ message: string }>()
const emit = defineEmits<{ (e: 'clicked', value: string): void }>()
function handleClick() {
emit('clicked', 'Button was clicked!')
}
</script>

6. Working with Refs and Reactive Data


ref and reactive are core to managing state in Vue 3. With TypeScript, you can
specify types.
typescriptCopyEditimport { ref, reactive } from 'vue'
const count = ref<number>(0)
interface Todo {
id: number
text: string
done: boolean
}
const todos = reactive<Todo[]>([
{ id: 1, text: 'Learn Vue', done: false },
{ id: 2, text: 'Use TypeScript', done: true }
])
7. Composables and Custom Composables
You can create composables (reusable logic) with TypeScript:
typescriptCopyEdit// useCounter.ts
import { ref } from 'vue'
export function useCounter() {
const count = ref<number>(0)
const increment = () => count.value++
return { count, increment }
}

8. Using Vue Router with TypeScript


You can strongly type your routes and guards:
typescriptCopyEditimport { createRouter, createWebHistory, RouteRecordRaw }
from 'vue-router'
const routes: RouteRecordRaw[] = [
{ path: '/', component: () => import('./views/Home.vue') },
{ path: '/about', component: () => import('./views/About.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router

9. Error Handling in TypeScript


TypeScript makes error handling clearer:
typescriptCopyEdittry {
const response = await fetch('/api/data')
if (!response.ok) throw new Error('Failed to fetch')
} catch (error) {
console.error((error as Error).message)
}

1. Introduction to Testing in Vue


There are three types of tests commonly used in Vue apps:
• Unit Tests: Test individual functions or components in isolation.
• Integration Tests: Test how components interact with each other.
• End-to-End (E2E) Tests: Simulate user behavior across the entire app.
In Vue, testing is made simpler with tools like Vitest and Jest. Let’s explore both.

2. Setting Up Vitest and Jest


Vitest Setup (Recommended with Vite)
If you're using Vite, Vitest is a perfect fit due to its speed and integration with
Vite.
Install Vitest:
bashCopyEditnpm install -D vitest @vue/test-utils jsdom
Add a vitest.config.ts file:
typescriptCopyEditimport { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: 'jsdom'
}
})
Run tests:
bashCopyEditnpx vitest
Jest Setup (For Non-Vite Projects)
If you’re using Vue CLI or prefer Jest:
Install Jest:
bashCopyEditnpm install -D jest @vue/test-utils babel-jest @babel/preset-env
@babel/core jsdom
Create a jest.config.js file:
javascriptCopyEditmodule.exports = {
testEnvironment: 'jsdom',
transform: {
'^.+\\.vue$': 'vue-jest',
'^.+\\.js$': 'babel-jest'
}
}
Run tests:
bashCopyEditnpx jest

3. Writing Unit Tests


Let’s test a simple utility function:
utils/calculate.ts:
typescriptCopyEditexport function add(a: number, b: number): number {
return a + b
}
tests/calculate.test.ts:
typescriptCopyEditimport { describe, it, expect } from 'vitest'
import { add } from '../utils/calculate'
describe('add()', () => {
it('adds two numbers', () => {
expect(add(2, 3)).toBe(5)
})
})
4. Testing Components
src/components/Counter.vue:
htmlCopyEdit<template>
<div>
<h1>{{ count }}</h1>
<button @click="increment">Increment</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
tests/Counter.test.ts:
typescriptCopyEditimport { mount } from '@vue/test-utils'
import Counter from '../src/components/Counter.vue'
import { describe, it, expect } from 'vitest'
describe('Counter.vue', () => {
it('increments count on button click', async () => {
const wrapper = mount(Counter)
expect(wrapper.text()).toContain('0')
await wrapper.find('button').trigger('click')
expect(wrapper.text()).toContain('1')
})
})

5. Testing Events and Emitted Data


You can check if a component emits events correctly:
src/components/CustomButton.vue:
htmlCopyEdit<template>
<button @click="$emit('clicked')">Click Me</button>
</template>
tests/CustomButton.test.ts:
typescriptCopyEditimport { mount } from '@vue/test-utils'
import CustomButton from '../src/components/CustomButton.vue'
test('emits "clicked" event', async () => {
const wrapper = mount(CustomButton)
await wrapper.trigger('click')
expect(wrapper.emitted()).toHaveProperty('clicked')
})

6. Testing Vue Router and Pinia


Testing Vue Router:
typescriptCopyEditimport { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../src/views/Home.vue'
import About from '../src/views/About.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
})
test('navigates to About page', async () => {
router.push('/about')
await router.isReady()
const wrapper = mount(About, { global: { plugins: [router] } })
expect(wrapper.text()).toContain('About Page')
})
7. Mocking Dependencies
Mocking is useful when components rely on API calls or services.
src/api/userApi.ts:
typescriptCopyEditexport async function fetchUser() {
return { id: 1, name: 'John Doe' }
}
tests/userApi.test.ts:
typescriptCopyEditimport { fetchUser } from '../src/api/userApi'
vi.mock('../src/api/userApi', () => ({
fetchUser: () => ({ id: 999, name: 'Mocked User' })
}))
test('fetches mocked user', async () => {
const user = await fetchUser()
expect(user.name).toBe('Mocked User')
})

8. Writing End-to-End (E2E) Tests


For E2E testing, tools like Cypress or Playwright are popular. Here’s a simple
example with Cypress:
Install Cypress:
bashCopyEditnpm install -D cypress
npx cypress open
cypress/integration/home.spec.js:
javascriptCopyEditdescribe('Home Page', () => {
it('Loads the home page', () => {
cy.visit('/')
cy.contains('Welcome to Vue')
})
})
1. What is Vue Router?
Vue Router is a library that lets you handle page navigation in a SPA without
needing full page reloads. Key features:
• Declarative Routing: Define routes as part of your component tree.
• Dynamic Routes: Pass parameters to routes dynamically.
• Navigation Guards: Control access to routes.
• Lazy Loading: Load components only when needed.
• History Modes: Choose between hash and history modes.

2. Setting Up Vue Router


Install Vue Router:
bashCopyEditnpm install vue-router
Create a router/index.ts file:
typescriptCopyEditimport { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
In main.ts:
typescriptCopyEditimport { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
3. Basic Routing
You can create navigation links with <router-link>:
htmlCopyEdit<template>
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<router-view></router-view>
</template>
Explanation:
• <router-link> generates anchor tags (<a>) with proper handling of clicks.
• <router-view> renders the matched component.

4. Dynamic Routes and Route Params


You can define dynamic routes to pass data as parameters:
router/index.ts:
typescriptCopyEditconst routes = [
{ path: '/user/:id', component: User }
]
User.vue:
htmlCopyEdit<template>
<h1>User ID: {{ $route.params.id }}</h1>
</template>
Navigate with:
htmlCopyEdit<router-link :to="`/user/${userId}`">View User</router-link>
5. Programmatic Navigation
You can navigate programmatically with router.push() or router.replace():
typescriptCopyEditimport { useRouter } from 'vue-router'
const router = useRouter()
function goToAbout() {
router.push('/about')
}

6. Navigation Guards
Navigation guards control access to routes. You can add guards globally, per
route, or inside components.
Global Guard:
typescriptCopyEditrouter.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isLoggedIn()) {
next('/login')
} else {
next()
}
})
Per Route Guard:
typescriptCopyEditconst routes = [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
}
]

7. Lazy Loading and Code Splitting


To improve performance, you can load components only when needed:
typescriptCopyEditconst routes = [
{ path: '/', component: () => import('../views/Home.vue') },
{ path: '/about', component: () => import('../views/About.vue') }
]

Vue will automatically split these into separate bundles.

8. Nested Routes
You can create parent-child routes:
typescriptCopyEditconst routes = [
{
path: '/user/:id',
component: User,
children: [
{ path: 'profile', component: UserProfile },
{ path: 'posts', component: UserPosts }
]
}
]
In User.vue:
htmlCopyEdit<template>
<h1>User</h1>
<router-view></router-view>
</template>

9. Handling 404 Pages and Redirects


Handle unknown routes with a wildcard *:
typescriptCopyEditconst routes = [
{ path: '/:pathMatch(.*)*', component: NotFound }
]
Redirects:
typescriptCopyEditconst routes = [
{ path: '/home', redirect: '/' }
]

10. Router with Pinia/State Management


Integrating Vue Router with Pinia is seamless. Here’s a simple example:
store/userStore.ts:
typescriptCopyEditimport { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({ isAuthenticated: false }),
actions: {
login() { this.isAuthenticated = true }
}
})
Navigation Guard:
typescriptCopyEditimport { useUserStore } from '../store/userStore'
router.beforeEach((to) => {
const userStore = useUserStore()
if (to.meta.requiresAuth && !userStore.isAuthenticated) {
return '/login'
}
})
1. What is i18n?
i18n stands for "Internationalization" — the process of making an app adaptable
to various languages and regions. The term "i18n" is shorthand for the 18 letters
between the i and n in "internationalization."
In Vue.js, Vue I18n handles translations, formatting, and pluralization in a clean
and maintainable way.
2. Setting Up Vue I18n
Install Vue I18n:
bashCopyEditnpm install vue-i18n
Create an i18n.ts file:
typescriptCopyEditimport { createI18n } from 'vue-i18n'
// Define translations
const messages = {
en: {
welcome: 'Welcome',
greeting: 'Hello, {name}!'
},
fr: {
welcome: 'Bienvenue',
greeting: 'Bonjour, {name} !'
}
}
// Create i18n instance
const i18n = createI18n({
locale: 'en', // Default language
fallbackLocale: 'en',
messages
})
export default i18n
Integrate i18n in main.ts:
typescriptCopyEditimport { createApp } from 'vue'
import App from './App.vue'
import i18n from './i18n'
const app = createApp(App)
app.use(i18n)
app.mount('#app')
3. Defining Locale Messages
You can define translations inline (as shown above) or in separate files:
locales/en.json:
jsonCopyEdit{
"welcome": "Welcome",
"greeting": "Hello, {name}!"
}
locales/fr.json:
jsonCopyEdit{
"welcome": "Bienvenue",
"greeting": "Bonjour, {name} !"
}
Update i18n.ts:
typescriptCopyEditimport en from './locales/en.json'
import fr from './locales/fr.json'
const messages = { en, fr }

4. Switching Languages Dynamically


In your components, you can switch languages dynamically:
htmlCopyEdit<template>
<h1>{{ $t('welcome') }}</h1>
<button @click="changeLanguage('en')">English</button>
<button @click="changeLanguage('fr')">Français</button>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
const { locale } = useI18n()
function changeLanguage(lang: string) {
locale.value = lang
}
</script>
5. Formatting Dates, Numbers, and Currencies
Vue I18n can handle date and number formatting based on locale:
htmlCopyEdit<template>
<p>{{ $d(new Date(), 'short') }}</p>
<p>{{ $n(123456.789, 'currency') }}</p>
</template>
In i18n.ts:
typescriptCopyEditconst i18n = createI18n({
locale: 'en',
messages,
datetimeFormats: {
en: { short: { year: 'numeric', month: 'short', day: 'numeric' } },
fr: { short: { year: 'numeric', month: 'long', day: 'numeric' } }
},
numberFormats: {
en: { currency: { style: 'currency', currency: 'USD' } },
fr: { currency: { style: 'currency', currency: 'EUR' } }
}
})

6. Handling Pluralization and Interpolation


Pluralization is built-in:
jsonCopyEdit{
"car": "car | cars",
"apples": "You have {count} apple | You have {count} apples"
}
Use it in templates:
htmlCopyEdit<p>{{ $tc('car', 1) }}</p> <!-- car -->
<p>{{ $tc('car', 2) }}</p> <!-- cars -->
<p>{{ $tc('apples', 1, { count: 1 }) }}</p> <!-- You have 1 apple -->
<p>{{ $tc('apples', 5, { count: 5 }) }}</p> <!-- You have 5 apples -->

7. Locale Fallback Strategy


If a translation is missing in the selected language, fallback to another:
typescriptCopyEditconst i18n = createI18n({
locale: 'en',
fallbackLocale: 'en', // Fallback to English if key is missing
messages
})

8. Organizing Translations in Large Projects


In larger projects, split translations into separate files:
pgsqlCopyEditsrc
├── locales
│ ├── en.json
│ ├── fr.json
└── i18n.ts
Lazy load translations:
typescriptCopyEditasync function loadLanguage(lang: string) {
const messages = await import(`./locales/${lang}.json`)
i18n.global.setLocaleMessage(lang, messages.default)
i18n.global.locale.value = lang
}

9. i18n with Router and Pinia


You can sync i18n with the router:
typescriptCopyEditrouter.beforeEach((to) => {
const lang = to.query.lang || 'en'
i18n.global.locale.value = lang
})
Or store the selected language in Pinia:
typescriptCopyEditimport { defineStore } from 'pinia'
export const useLangStore = defineStore('lang', {
state: () => ({ lang: 'en' }),
actions: {
setLang(lang: string) {
this.lang = lang
}
}
})

10. Testing i18n


Use Vitest or Jest to test i18n:
typescriptCopyEditimport { mount } from '@vue/test-utils'
import i18n from '../src/i18n'
import App from '../src/App.vue'
test('renders translated text', () => {
const wrapper = mount(App, {
global: { plugins: [i18n] }
})
expect(wrapper.text()).toContain('Welcome')
})

Structuring Large-Scale Vue Apps


When building large Vue applications, proper structure is key to maintaining
scalability, readability, and maintainability. As projects grow, having a clear
separation of concerns and modular design becomes crucial.
🏗️ 1. Why Structure Matters in Large Projects
In large-scale applications, a poor structure can result in:
• Difficulty in finding files and components.
• Code duplication and tight coupling.
• Reduced maintainability and collaboration.
A good structure:
Organizes code into logical modules.
Promotes reusability with composables, stores, and utilities.
Improves collaboration with clear separation of concerns.

📁 2. Recommended Project Structure


Here’s a typical structure for a large-scale Vue 3 project with Vite and TypeScript:
graphqlCopyEditsrc/
├── assets/ # Static assets (images, fonts, etc.)
├── components/ # Reusable UI components
│ ├── ui/ # UI-specific components (buttons, modals, etc.)
│ ├── layout/ # Layout components (header, footer)
├── composables/ # Reusable logic with Composition API
├── directives/ # Custom directives
├── i18n/ # Internationalization
├── layouts/ # Page layouts (optional)
├── pages/ # Views for each route
│ ├── dashboard/ # Nested feature modules
│ ├── auth/ # Login/Register pages
├── router/ # Vue Router setup
├── services/ # API calls and external services
├── store/ # State management (Pinia/Vuex)
├── types/ # TypeScript interfaces and types
├── utils/ # Utility functions/helpers
├── App.vue # Root component
├── main.ts # App entry point
└── vite.config.ts # Vite configuration
⚙️ 3. Component Organization
In large apps, split components by feature/module:
mathematicaCopyEditcomponents/
├── ui/
│ ├── Button.vue
│ ├── Modal.vue
├── dashboard/
│ ├── DashboardCard.vue
│ ├── DashboardHeader.vue

🧩 4. Composables for Reusability


Use composables to extract reusable logic:
composables/useFetch.ts
typescriptCopyEditimport { ref, onMounted } from 'vue'
export function useFetch(url: string) {
const data = ref(null)
const error = ref(null)
onMounted(async () => {
try {
const res = await fetch(url)
data.value = await res.json()
} catch (err) {
error.value = err
}
})
return { data, error }
}

🌐 5. Store Management (Pinia/Vuex)


Centralize state management in a store/ directory:
store/userStore.ts
typescriptCopyEditimport { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({ isAuthenticated: false }),
actions: {
login() {
this.isAuthenticated = true
}
}
})

🔌 6. Modular Routing
Keep routes organized in router/ directory:
router/index.ts
typescriptCopyEditimport { createRouter, createWebHistory } from 'vue-router'
import Dashboard from '../pages/Dashboard.vue'
import Login from '../pages/Login.vue'
const routes = [
{ path: '/', component: Dashboard },
{ path: '/login', component: Login }
]
export const router = createRouter({
history: createWebHistory(),
routes
})

📈 7. Environment Variables
Organize environment-specific settings in .env files:
bashCopyEdit.env
.env.production
.env.development
.env:
iniCopyEditVITE_API_URL=https://api.example.com
Access it:
typescriptCopyEditconsole.log(import.meta.env.VITE_API_URL)

⚙️ Environment Variables and Configs


In large applications, environment-specific configurations make your app
adaptable to different environments like development, staging, and production.

📌 1. Why Use Environment Variables?


Separate environment-specific values.
Avoid hardcoding sensitive data.
Enable easier deployment in different environments.

📁 2. Setting Up Environment Variables


Create environment files:
bashCopyEdit.env # Shared across all environments
.env.development # Development environment
.env.production # Production environment
.env:
iniCopyEditVITE_API_URL=https://api.example.com
VITE_APP_NAME=MyVueApp

🏗️ 3. Accessing Environment Variables


In vite.config.ts:
typescriptCopyEditimport { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
define: {
__APP_NAME__: JSON.stringify(process.env.VITE_APP_NAME)
},
plugins: [vue()]
})
Use in your app:
typescriptCopyEditconsole.log(import.meta.env.VITE_API_URL)

🚀 CI/CD and Deployment Strategies


Continuous Integration and Continuous Deployment (CI/CD) streamline code
integration, testing, and deployment into production.

⚙️ 1. What is CI/CD?
• Continuous Integration (CI): Automate testing and integration of code into
a shared repository.
• Continuous Deployment (CD): Automatically deploy tested code to
production.

📋 2. Setting Up GitHub Actions for CI/CD


Create .github/workflows/deploy.yml:
yamlCopyEditname: Deploy Vue App
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Install Dependencies
run: npm install
- name: Build Project
run: npm run build
- name: Deploy to GitHub Pages
run: npm run deploy

📤 3. Deployment to Vercel
1. Install Vercel CLI:
bashCopyEditnpm i -g vercel
1. Deploy:
bashCopyEditvercel

📈 4. Other Deployment Options


• Netlify: Supports direct Git integration.
• GitHub Pages: Use vite-plugin-gh-pages for easy deployment.
• Docker: Containerize your app with a simple Dockerfile.
dockerfileCopyEditFROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install && npm run build
CMD ["npm", "run", "preview"]

You might also like