A Brief History of Vue.js (How It All Began)
Vue.js was created by Evan You in 2014.
He wanted a framework that combined React’s reactivity with Angular’s templating—but without the complexity.
Vue.js started as a personal project but quickly became one of the most loved frontend frameworks.
Here’s how Vue.js has evolved over time:
Version | Release Date | Notable Features |
---|
0.6 | 2014-02-01 | Initial release, basic reactivity |
1.0 | 2015-10-27 | Directives, Components, Vue Router support |
2.0 | 2016-09-30 | Virtual DOM, Improved Reactivity, Vuex |
2.5 | 2017-10-13 | TypeScript support, Better SSR |
3.0 | 2020-09-18 | Composition API, Fragments, Teleport |
3.2 | 2021-08-05 | Performance improvements, script setup |
1. The Composition API (Vue 3)
The Composition API makes it easier to organize logic in Vue components.
1
2
3
4
5
6
7
8
| import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
return { count };
},
};
|
2. Vue Directives (v-bind
, v-if
, v-for
)
Directives make Vue declarative and expressive.
1
2
3
4
| <p v-if="user">Hello, {{ user.name }}!</p>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
|
3. Two-Way Data Binding (v-model
)
No need for complicated state management—Vue does it natively.
1
2
| <input v-model="message" />
<p>{{ message }}</p>
|
4. Vue Components (Reusable UI Blocks)
Vue encourages breaking UIs into small, reusable components.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Button.vue
<template>
<button @click="onClick">Click me</button>
</template>
<script>
export default {
methods: {
onClick() {
alert('Button clicked!');
}
}
};
</script>
|
5. Vue Router (Navigation Made Easy)
Vue Router enables SPA navigation without full-page reloads.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // router.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from './pages/Home.vue';
import About from './pages/About.vue';
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
];
export default createRouter({
history: createWebHistory(),
routes,
});
|
6. Vuex (State Management)
For complex applications, Vuex keeps shared state organized.
1
2
3
4
5
6
7
8
9
10
| import { createStore } from 'vuex';
export default createStore({
state: { count: 0 },
mutations: {
increment(state) {
state.count++;
},
},
});
|
7. Lifecycle Hooks (mounted
, updated
, beforeUnmount
)
Execute code at different stages of a component’s life.
1
2
3
4
5
| export default {
mounted() {
console.log('Component is mounted!');
}
};
|
8. Vue’s Teleport API (Move Elements Anywhere)
Teleport lets you render UI outside of the root app container.
1
2
3
| <teleport to="body">
<div class="modal">Hello from teleport!</div>
</teleport>
|
9. Scoped Styles (Avoid CSS Conflicts)
Keep styles isolated to a specific component.
1
2
3
4
5
| <style scoped>
h1 {
color: red;
}
</style>
|
10. Using watch
to React to Data Changes
Run logic when a reactive value changes.
1
2
3
4
5
6
| import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`);
});
|
Computed properties cache results and recalculate only when dependencies change.
1
2
3
4
5
6
7
8
9
10
| export default {
data() {
return { firstName: 'John', lastName: 'Doe' };
},
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
};
|
12. Using Watchers to React to Data Changes
Watchers allow you to run logic whenever a data property changes.
1
2
3
4
5
6
7
8
9
10
| export default {
data() {
return { count: 0 };
},
watch: {
count(newValue, oldValue) {
console.log(`Count changed from ${oldValue} to ${newValue}`);
}
}
};
|
13. Conditional Rendering with v-show
Use v-show
instead of v-if
when toggling visibility without removing elements from the DOM.
1
2
| <p v-show="isVisible">I am visible!</p>
<button @click="isVisible = !isVisible">Toggle</button>
|
14. Using Slots for Reusable Components
Slots allow you to pass custom content into components.
1
2
3
4
5
6
7
8
9
10
11
12
| <!-- Parent.vue -->
<Child>
<template v-slot:header>
<h1>Custom Header</h1>
</template>
</Child>
<!-- Child.vue -->
<template>
<slot name="header"></slot>
<p>Default content</p>
</template>
|
15. Emitting Custom Events
Child components can emit events to their parent.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <!-- Child.vue -->
<template>
<button @click="$emit('customEvent', 'Hello from Child!')">Click me</button>
</template>
<!-- Parent.vue -->
<Child @customEvent="handleEvent" />
<script>
export default {
methods: {
handleEvent(msg) {
console.log(msg);
}
}
};
</script>
|
16. Using Key Modifiers in Event Handling
Key modifiers simplify keyboard event handling.
1
| <input @keyup.enter="submitForm" placeholder="Press Enter to submit" />
|
17. Transition Effects with Vue
Vue makes animations and transitions easy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <template>
<transition name="fade">
<p v-if="isVisible">Hello, Vue!</p>
</transition>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
|
Filters allow quick formatting of text.
1
2
3
4
5
6
7
8
9
10
11
12
13
| <template>
<p>{{ price | currency }}</p>
</template>
<script>
export default {
filters: {
currency(value) {
return `$${value.toFixed(2)}`;
}
}
};
</script>
|
19. Fetching API Data with fetch
and Vue’s Lifecycle Hooks
Make API requests on component mount.
1
2
3
4
5
6
7
8
9
| export default {
data() {
return { posts: [] };
},
async mounted() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
this.posts = await res.json();
}
};
|
20. Creating Custom Directives
Custom directives let you extend Vue’s functionality.
1
2
3
4
5
6
7
8
9
| // Register a global directive
Vue.directive('focus', {
inserted(el) {
el.focus();
}
});
<!-- Usage -->
<input v-focus />
|
21. Dynamic Components with :is
Attribute
Easily switch between components dynamically.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| <template>
<component :is="currentComponent"></component>
<button @click="toggleComponent">Toggle Component</button>
</template>
<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default {
data() {
return { currentComponent: 'ComponentA' };
},
components: { ComponentA, ComponentB },
methods: {
toggleComponent() {
this.currentComponent = this.currentComponent === 'ComponentA' ? 'ComponentB' : 'ComponentA';
}
}
};
</script>
|
22. Using Pinia
for State Management (Vuex Alternative)
Pinia is a modern and lightweight state management solution for Vue.
1
2
3
4
5
6
7
8
9
10
11
| // store.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++;
}
}
});
|
23. Using Vue Teleport to Render Outside the Root Component
Teleport allows rendering outside of the Vue root node.
1
2
3
4
5
| <template>
<teleport to="body">
<div class="modal">This is a modal rendered outside the app root</div>
</teleport>
</template>
|
Prevent excessive API calls by debouncing user input.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <template>
<input v-model="searchQuery" @input="debouncedSearch" />
</template>
<script>
import { ref } from 'vue';
import { debounce } from 'lodash';
export default {
setup() {
const searchQuery = ref('');
const debouncedSearch = debounce(() => {
console.log(`Searching for: ${searchQuery.value}`);
}, 500);
return { searchQuery, debouncedSearch };
}
};
</script>
|
25. Using Vue Composables for Reusable Logic
Extract reusable logic into a composable function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // useMouse.js
import { ref, onMounted, onUnmounted } from 'vue';
export function useMouse() {
const x = ref(0);
const y = ref(0);
function updateMouse(event) {
x.value = event.clientX;
y.value = event.clientY;
}
onMounted(() => window.addEventListener('mousemove', updateMouse));
onUnmounted(() => window.removeEventListener('mousemove', updateMouse));
return { x, y };
}
|
26. Server-Side Rendering (SSR) with Nuxt.js
Improve SEO and performance with SSR.
1
2
3
4
5
6
7
8
9
10
11
12
| // pages/index.vue
<template>
<h1>{{ message }}</h1>
</template>
<script>
export default {
async asyncData() {
return { message: 'Hello from SSR!' };
}
};
</script>
|
Reduce initial bundle size by lazy-loading components.
1
2
3
4
5
6
7
| import { defineAsyncComponent } from 'vue';
export default {
components: {
LazyComponent: defineAsyncComponent(() => import('./LazyComponent.vue'))
}
};
|
28. Using nextTick
to Wait for DOM Updates
Ensure Vue updates the DOM before running logic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import { nextTick, ref } from 'vue';
export default {
setup() {
const count = ref(0);
async function increment() {
count.value++;
await nextTick();
console.log('DOM updated!', count.value);
}
return { count, increment };
}
};
|
29. Using Vue’s Built-in provide/inject
for Dependency Injection
Share state without prop drilling.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // Parent.vue
<template>
<Child />
</template>
<script>
import { provide, ref } from 'vue';
import Child from './Child.vue';
export default {
setup() {
const sharedData = ref('Hello from parent');
provide('sharedData', sharedData);
},
components: { Child }
};
</script>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // Child.vue
<template>
<p>{{ sharedData }}</p>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const sharedData = inject('sharedData');
return { sharedData };
}
};
</script>
|
30. Using Render Functions for Dynamic UI Generation
Vue allows you to dynamically generate templates with render functions.
1
2
3
4
5
6
7
| export default {
render() {
return h('button', {
onClick: () => alert('Clicked!')
}, 'Click Me');
}
};
|
31. Creating a Custom Directive for Lazy Loading Images
Improve performance by lazy-loading images only when they are visible.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // directives/lazyLoad.js
export default {
beforeMount(el, binding) {
const loadImage = () => {
el.src = binding.value;
};
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
loadImage();
observer.unobserve(el);
}
});
observer.observe(el);
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <!-- Usage -->
<template>
<img v-lazy-load="imageSrc" alt="Lazy loaded image" />
</template>
<script>
import LazyLoadDirective from '@/directives/lazyLoad';
export default {
directives: { lazyLoad: LazyLoadDirective },
data() {
return { imageSrc: 'https://example.com/image.jpg' };
}
};
</script>
|
32. Using Web Workers in Vue for Heavy Computation
Offload heavy computations to a web worker to keep the UI responsive.
1
2
3
4
5
| // worker.js
self.onmessage = function (e) {
const result = e.data.num * 2;
self.postMessage(result);
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| <template>
<div>
<button @click="runWorker">Compute</button>
<p>Result: {{ result }}</p>
</div>
</template>
<script>
export default {
data() {
return { result: null };
},
methods: {
runWorker() {
const worker = new Worker(new URL('@/worker.js', import.meta.url));
worker.postMessage({ num: 10 });
worker.onmessage = (e) => {
this.result = e.data;
};
}
}
};
</script>
|
Generate and handle dynamic forms efficiently.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| <template>
<form @submit.prevent="submitForm">
<div v-for="(field, index) in formFields" :key="index">
<label :for="field.name">{{ field.label }}</label>
<input v-model="formData[field.name]" :type="field.type" />
</div>
<button type="submit">Submit</button>
</form>
</template>
<script>
export default {
data() {
return {
formFields: [
{ name: 'username', label: 'Username', type: 'text' },
{ name: 'email', label: 'Email', type: 'email' }
],
formData: {}
};
},
methods: {
submitForm() {
console.log(this.formData);
}
}
};
</script>
|
34. Using Async Components for Faster Load Times
Dynamically import Vue components to improve performance.
1
2
3
4
5
6
7
| import { defineAsyncComponent } from 'vue';
export default {
components: {
HeavyComponent: defineAsyncComponent(() => import('@/components/HeavyComponent.vue'))
}
};
|
35. Implementing Route Authentication Guards
Secure routes using Vue Router guards.
1
2
3
4
5
6
7
8
9
| // router.js
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('auth');
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login');
} else {
next();
}
});
|
36. Creating a Throttled Event Listener
Improve performance by throttling event listeners.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import { throttle } from 'lodash';
export default {
methods: {
handleScroll: throttle(() => {
console.log('Scrolled!');
}, 200)
},
mounted() {
window.addEventListener('scroll', this.handleScroll);
},
beforeUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
};
|
37. Encrypting and Decrypting Data in Vue.js
Use AES encryption to secure user data.
1
2
3
4
5
6
7
8
9
10
11
| import CryptoJS from 'crypto-js';
const secretKey = 'my-secret-key';
export function encrypt(data) {
return CryptoJS.AES.encrypt(data, secretKey).toString();
}
export function decrypt(ciphertext) {
return CryptoJS.AES.decrypt(ciphertext, secretKey).toString(CryptoJS.enc.Utf8);
}
|
38. Using Vue to Detect Dark Mode Preferences
Automatically detect and switch themes based on system preferences.
1
2
3
4
5
6
7
8
9
10
| export default {
data() {
return { isDarkMode: window.matchMedia('(prefers-color-scheme: dark)').matches };
},
watch: {
isDarkMode(newValue) {
document.body.classList.toggle('dark-mode', newValue);
}
}
};
|
39. Using Vue with GraphQL
Fetch data using GraphQL with Vue.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| import { useQuery } from '@vue/apollo-composable';
import gql from 'graphql-tag';
const GET_USERS = gql`{
users {
id
name
}
}`;
export default {
setup() {
const { result, loading } = useQuery(GET_USERS);
return { result, loading };
}
};
|
40. Implementing File Upload in Vue
Handle file uploads in Vue components.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <template>
<input type="file" @change="uploadFile" />
</template>
<script>
export default {
methods: {
uploadFile(event) {
const file = event.target.files[0];
const formData = new FormData();
formData.append('file', file);
fetch('/upload', { method: 'POST', body: formData });
}
}
};
</script>
|
References