Vue.js Fundamentals: A Complete Guide to Getting Started
Introduction to Vue.js
Vue.js is a progressive frontend framework built around the MVVM (Model-View-ViewModel) architectural pattern. Similar to Angular, it provides a clean and intuitive approach to building user interfaces. Vue.js is designed to be incrementally adoptable, meaning you can start with basic features and scale up to more complex applications as needed.
Setting Up Vue.js via CDN
While you can download Vue.js files directly and reference them locally, using a CDN (Content Delivery Network) offers significant advantages. CDN services host popular libraries and serve them from geographically distributed servers, providing faster loading times and reducing the bandwidth usage of your local project.
One reliable CDN option is BootCDN, which provides access to numerous frontend libraries and plugins. When using CDN-hosted resources, ensure you have an active internet connection when running your application.
Your First Vue.js Application
Vue.js applications revolve around two main concepts: a template (the view layer) and a Vue instance (the ViewModel layer that connects your data to the DOM). The template serves as the container where Vue will render dynamic content, while the Vue instance manages the application state and behavior.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue.js Demo</title>
<script src="https://cdn.bootcss.com/vue/2.5.9/vue.js"></script>
</head>
<body>
<h1 id="app-container">{{message}}</h1>
<script>
var vueInstance = new Vue({
el: "#app-container",
data: {
message: "Hello, Vue.js!"
}
});
</script>
</body>
</html>
When this page loads, Vue.js replaces the {{message}} placeholder with the actual value from the data object, resulting in "Hello, Vue.js!" being displayed. The syntax using double curly braces is called mustache interpolation and serves as Vue's template syntax for rendering data values.
Expression Evaluation in Templates
Vue's template system supports JavaScript expressions within double curly braces. You can perform basic arithmetic operations, use ternary operators, and call methods that return values. However, complex logic should be moved to computed properties or methods for better maintainability.
<div>{{100 / 2 * 8}}</div>
<div>{{gender === 1 ? "Male" : "Female"}}</div>
Binding HTML Attributes with v-bind
While mustache syntax works for text content, binding data to HTML attributes requires the v-bind directive. This directive dynamically assigns values to HTML attributes, enabling your templates to respond to changing data.
<div id="container" :href="url">Clickable Link</div>
<script>
var app = new Vue({
el: "#container",
data: {
url: "https://example.com"
}
});
</script>
The v-bind directive accepts an HTML attribute as its argument and binds it to an expression in your data. In practice, you can omit v-bind: and use the shorthand : directly, as shown in the example above. This shorthand is widely used and considered a best practice in Vue.js development.
Handling Events with v-on
To make your application interactive, you need to respond to user actions such as clicks, keypresses, and form submissions. The v-on directive attaches event listeners to DOM elements and triggers specified methods when those events occur.
<div id="container" :href="url">
<button @click="reverseText">Reverse Text</button>
</div>
<script>
var app = new Vue({
el: "#container",
data: {
message: "Sample Text"
},
methods: {
reverseText: function() {
this.message = this.message.split('').reverse().join('');
}
}
});
</script>
Similar to v-bind, the v-on directive has a shorthand notasion. Simply replace v-on:click with @click for cleaner and more readable templates. The methods defined in the methods object have access to the Vue instance's data and other methods through this.
Two-Way Data Binding with v-model
The examples so far demonstrate one-way data binding, where changes in data flow to the view. For scenarios where user input should update the underlying data, Vue.js provides the v-model directive, which creates a two-way binding between form inputs and your application state.
<div id="container">
<input type="text" v-model="userInput">
<button @click="reverseInput">Reverse</button>
<p>Reversed: {{reversedInput}}</p>
</div>
<script>
var app = new Vue({
el: "#container",
data: {
userInput: "Enter text here"
},
methods: {
reverseInput: function() {
this.userInput = this.userInput.split('').reverse().join('');
}
}
});
</script>
The v-model directive automatically keeps the input value synchronized with the bound data property. As the user types, the userInput value updates automatically, and any other part of your template using this value will reflect the changes immediately.
Computed Properties
While you can use expressions directly in templates for simple operations, complex calculations belong in computed properties. Computed properties are cached based on their dependencies and only recalculate when those dependencies change, making them more efficient than methods for derived data.
<div id="container">
<p>{{calculationResult}}</p>
</div>
<script>
var app = new Vue({
el: "#container",
computed: {
calculationResult: function() {
var firstValue = 10;
var secondValue = 5;
if (firstValue + secondValue < 20) {
return "Sum is less than 20";
} else {
return "Sum is greater than or equal to 20";
}
}
}
});
</script>
Computed properties are defined as functions that return a value. They can access the Vue instance's data through this and automatically update when any referenced data changes. Computed properties also support getter and setter functions for more control over how values are read and written.
Watchers for Reactive Data Changes
While computed properties are ideal for derived data, watchers provide a more generic way to react to data changes. Watchers are perfect for scenarios where you need to perform asynchronous operations, make API calls, or execute arbitrary code when a specific data property changes.
<input type="text" v-model="username">
<p>{{validationMessage}}</p>
<script>
var app = new Vue({
el: "#container",
data: {
username: "",
validationMessage: ""
},
watch: {
username: function(newValue) {
if (newValue.length < 6) {
this.validationMessage = "Username must be at least 6 characters";
} else {
this.validationMessage = "Username is valid";
}
}
}
});
</script>
Watchers are defined in the watch object, where each key corresponds to a data property you want to monitor. When the watched property changes, Vue calls the associated function with the new and old values as arguments.
Conditional Rendering
Vue.js provides several directives for conditionally rendering elements: v-if, v-else-if, v-else, and v-show. These directives control whether elements are included in the DOM based on the truthiness of expressions.
<div v-if="status === 'active'">Content is active</div>
<div v-else-if="status === 'pending'">Content is pending</div>
<div v-else>Content is inactive</div>
The key difference between v-if and v-show is that v-if completely adds or removes elements from the DOM, while v-show uses CSS display properties to toggle visibility. Use v-if for conditions that rarely change, as it has higher toggle costs. Use v-show for frequently toggled elements.
List Rendering with v-for
To render lists of elements, use the v-for directive. It iterates over an array or object and creates a template instance for each item, providing access to both the item value and its index.
<div id="container">
<div v-for="(item, index) in itemList">
Item {{index + 1}}: {{item}}
</div>
</div>
<script>
var app = new Vue({
el: "#container",
data: {
itemList: ["Apple", "Banana", "Cherry", "Date", "Elderbery"]
}
});
</script>
When using v-for with objects, the iteration order follows the same order as Object.keys(). For optimal rendering performance, always provide a unique :key attribute bound to a stable identifier for each item.
Creating Custom Directives
Vue.js includes several built-in directives, but you can extend Vue's capabilities by creating custom directives. Custom directives are useful for low-level DOM manipulations that reusable components don't adequately address. The Vue.directive method registers a global custom directive.
<div id="app">
<div v-custom-directive="sizeValue">Styled Text</div>
<button @click="increaseSize">Increase Size</button>
</div>
<script>
Vue.directive('custom-directive', {
bind: function(element, binding) {
element.style.fontSize = binding.value + 'px';
}
});
var app = new Vue({
el: "#app",
data: {
sizeValue: 16
},
methods: {
increaseSize: function() {
this.sizeValue += 2;
}
}
});
</script>
When defining custom directives, omit the v- prefix in the directive name but include it when using the directive in templates. The directive function receives the element, binding object, and virtual node as parameters, providing everything needed for DOM manipulation.
Directive Hooks
Custom directives have a lifecycle with five hooks that you can use to control behavior at different stages:
- bind: Called once when the directive is first bound to the element. Use for one-time initialization.
- inserted: Called when the bound element is inserted into its parent node.
- update: Called when the component containing the directive updates, regardless of whether the bound value changed.
- componentUpdated: Called after the component and its children have updated.
- unbind: Called once when the directive is unbound from the element.
Vue.directive('lifecycle-demo', {
bind: function() {
console.log('1 - Directive bound');
},
inserted: function() {
console.log('2 - Element inserted in DOM');
},
update: function() {
console.log('3 - Directive value updated');
},
componentUpdated: function() {
console.log('4 - Component and children updated');
},
unbind: function() {
console.log('5 - Directive unbound');
}
});
Vue.extend and Component Templates
Vue.extend creates a reusable component definition that can be mounted to DOM elements. While modern Vue.js development typically uses the component option directly, understanding Vue.extend provides insight into Vue's component system.
<div id="external-link"></div>
<script>
var linkTemplate = Vue.extend({
template: '<a :href="url">{{anchorText}}</a>',
data: function() {
return {
url: "https://www.example.com",
anchorText: "Visit Website"
};
}
});
new linkTemplate().$mount('#external-link');
</script>
Passing Data with propsData
When you need to pass data to a component at instantiation time rather than having it defined internally, use the propsData option. This approach allows components to be more flexible and reusable across different contexts.
<div id="external-link"></div>
<script>
var dynamicLink = Vue.extend({
template: '<a :href="websiteUrl">{{linkText}}</a>',
props: ['websiteUrl', 'linkText']
});
new dynamicLink({
propsData: {
websiteUrl: "https://www.example.com",
linkText: "Click Here"
}
}).$mount('#external-link');
</script>
Props defined in the component accept external values passed through propsData. This pattern is essential for building reusable components that can display different content based on their configuration.
Creating Reusable Components
Components are the foundation of Vue.js application architecture. They encapsulate templates, logic, and styles into self-contained, reusable units. Global components are registered using Vue.component and are available throughout your entire application.
<custom-element></custom-element>
<script>
Vue.component('custom-element', {
template: "<div>This is a reusable component</div>"
});
</script>
The template property defines what the component renders. Any HTML valid within the component's placement location can be included. Components become significantly more useful when they can accept dynamic data through props.
<list-item v-for="(value, index) in numbers" :item-data="value"></list-item>
<script>
Vue.component('list-item', {
props: ['itemData'],
template: "<div>{{itemData}}</div>"
});
var app = new Vue({
el: "#container",
data: {
numbers: [1, 2, 3, 4, 5]
}
});
</script>
Handling Props with Hyphens
When defining props that correspond to HTML attributes containing hyphens (kebab-case), use camelCase in the props array. Vue automatically converts between the two formats during template parsing.
<panda origin-country="China"></panda>
<script>
Vue.component('panda', {
props: ['originCountry'],
template: "<div>Panda from {{originCountry}}</div>"
});
</script>
Local Component Registration
For better code organization and to avoid polluting the global scope, register components locally within specific Vue instances. Local components are only available within the Vue instances where they are registered, preventing naming conflicts in larger applications.
<div id="application">
<dynamic-link :source="website"></dynamic-link>
</div>
<script>
var linkComponent = {
template: '<a :href="source">{{source}}</a>',
props: ['source']
};
var app = new Vue({
el: "#application",
data: {
website: "https://www.example.com"
},
components: {
"dynamic-link": linkComponent
}
});
</script>
Note that local registration uses the components property (with an "s") rather than the global component method. This distinction is important when migrating between registration approaches.
Nesting Components
Components can contain other components, creating a hierarchical structure that mirrors the document object model. Child components are defined in their parent's components option, enabling parent-child communication through props and events.
<div id="application">
<parent-container :website="url"></parent-container>
</div>
<script>
var childElement = {
template: '<div>Child Component Content</div>'
};
var parentComponent = {
template: '<a :href="website">Parent Link <child-element></child-element></a>',
props: ['website'],
components: {
"child-element": childElement
}
};
var app = new Vue({
el: "#application",
data: {
url: "https://www.example.com"
},
components: {
"parent-container": parentComponent
}
});
</script>
Remember that component templates require a single root element. If you need multiple root-level elements, wrap them in a container element to satisfy Vue's template requirements.
Dynamic Component Switching
The <component> element combined with the is attribute enables dynamic component switching. This powerful feature allows you to conditionally render different components based on application state, enabling tabbed interfaces and other UI patterns.
<div id="application">
<component :is="currentComponent"></component>
<button @click="switchComponent">Next Component</button>
</div>
<script>
var componentA = {
template: '<div>Component A Content</div>'
};
var componentB = {
template: '<div>Component B Content</div>'
};
var componentC = {
template: '<div>Component C Content</div>'
};
var app = new Vue({
el: "#application",
data: {
currentComponent: 'componentA'
},
components: {
"componentA": componentA,
"componentB": componentB,
"componentC": componentC
},
methods: {
switchComponent: function() {
if (this.currentComponent === 'componentA') {
this.currentComponent = 'componentB';
} else if (this.currentComponent === 'componentB') {
this.currentComponent = 'componentC';
} else {
this.currentComponent = 'componentA';
}
}
}
});
</script>
Dynamic components are particularly useful for building wizard-style interfaces, modal dialogs, and tab navigation systems where only one component should be visible at a time while maintaining component state between switches.
Virtual DOM and Vue Instances
Vue.js uses a virtual DOM representation to efficiently update the actual DOM. When data changes, Vue calculates the differences between virtual DOM snapshots and applies only the necessary updates to the real DOM. This approach balances the flexibility of direct DOM manipulation with the performance benefits of virtual DOM diffing.
The Vue instance acts as the bridge between your data and the DOM. It proxies access to data properties, makes methods available through this, and automatically updates the DOM when reactive data changes. Understanding this relationship is fundamental to building effective Vue applications.