Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork644
List of 300 VueJS Interview Questions And Answers
sudheerj/vuejs-interview-questions
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
List of 300 VueJS Interview Questions
Click ⭐if you like the project. Pull Requests are highly appreciated. Followme@SudheerJonna for technical updates.
- Take thisVue.js course to go from a complete Vue beginner to confidently building large-scale applications from scratch
- Take thiscoding interview bootcamp if you're serious about getting hired and don't have a CS degree
Vue.js is an open-source, progressive Javascript framework for building user interfaces that aim to beincrementally adoptable. The core library of VueJS is focused on the
view layer
only, and is easy to pick up andintegrate with other libraries or existing projects.Below are the some of major features available with VueJS
- Virtual DOM: It uses virtual DOM similar to other existing frameworks such as ReactJS, Ember etc. Virtual DOMis a light-weight in-memory tree representation of the original HTML DOM and updated without affecting theoriginal DOM.
- Components: Used to create reusable custom elements in VueJS applications.
- Templates: VueJS provides HTML based templates that bind the DOM with the Vue instance data
- Routing: Navigation between pages is achieved through vue-router
- Light weight: VueJS is light weight library compared to other frameworks.
Lifecycle hooks are a window into how the library you're using works behind-the-scenes. By using these hooks, youwill know when your component is created, added to the DOM, updated, or destroyed. Let's look at lifecycle diagrambefore going to each lifecycle hook in detail,
- Creation(Initialization):Creation Hooks allow you to perform actions before your component has even been added to the DOM. You need to usethese hooks if you need to set things up in your component both during client rendering and server rendering.Unlike other hooks, creation hooks are also run during server-side rendering.
- beforeCreate:This hook runs at the very initialization of your component. hook observes data and initialization events inyour component. Here, data is still not reactive and events that occur during the component's lifecycle havenot been set up yet.
newVue({data:{count:10},beforeCreate:function(){console.log('Nothing gets called at this moment')// `this` points to the view model instanceconsole.log('count is '+this.count);}})// count is undefined
- created:This hook is invoked when Vue has set up events and data observation. Here, events are active and access toreactive data is enabled though templates have not yet been mounted or rendered.
Note: Remember that, You will not have access to the DOM or the target mounting element (this.$el) inside ofcreation hooksnewVue({data:{count:10},created:function(){// `this` points to the view model instanceconsole.log('count is: '+this.count)}})// count is: 10
- Mounting(DOM Insertion):Mounting hooks are often the most-used hooks and they allow you to access your component immediately before andafter the first render.
- beforeMount:The beforeMount allows you to access your component immediately before and after the first render.
newVue({beforeMount:function(){// `this` points to the view model instanceconsole.log(`this.$el is yet to be created`);}})
- mounted:This is a most used hook and you will have full access to the reactive component, templates, and renderedDOM (via. this.$el). The most frequently used patterns are fetching data for your component.
<divid="app"><p>I'm text inside the component.</p></div>newVue({el:'#app',mounted:function(){console.log(this.$el.textContent);// I'm text inside the component.}})
- Updating (Diff & Re-render):Updating hooks are called whenever a reactive property used by your component changes, or something else causesit to re-render
- beforeUpdate:The beforeUpdate hook runs after data changes on your component and the update cycle begins, right before theDOM is patched and re-rendered.
<divid="app"><p>{{counter}}</p></div>...// rest of the codenewVue({el:'#app',data(){return{counter:0}},created:function(){setInterval(()=>{this.counter++},1000)},beforeUpdate:function(){console.log(this.counter)// Logs the counter value every second, before the DOM updates.}})
- updated:This hook runs after data changes on your component and the DOM re-renders.
<divid="app"><pref="dom">{{counter}}</p></div>...//newVue({el:'#app',data(){return{counter:0}},created:function(){setInterval(()=>{this.counter++},1000)},updated:function(){console.log(+this.$refs['dom'].textContent===this.counter)// Logs true every second}})
- Destruction (Teardown):Destruction hooks allow you to perform actions when your component is destroyed, such as cleanup or analyticssending.
- beforeDestroy:
beforeDestroy
is fired right before teardown. If you need to cleanup events or reactive subscriptions,beforeDestroy would probably be the time to do it. Your component will still be fully present and functional.
newVue({data(){return{message:'Welcome VueJS developers'}},beforeDestroy:function(){this.message=nulldeletethis.message}})
- destroyed:This hooks is called after your component has been destroyed, its directives have been unbound and its eventlisteners have been removed.
newVue({destroyed:function(){console.log(this)// Nothing to show here}})
- beforeDestroy:
- Creation(Initialization):Creation Hooks allow you to perform actions before your component has even been added to the DOM. You need to usethese hooks if you need to set things up in your component both during client rendering and server rendering.Unlike other hooks, creation hooks are also run during server-side rendering.
The Vue components can be created in two different API styles
Options API: The Options API uses component logic using an object of options such as
data
,props
,computed
,methods
and life cycle methods etc. The properties will be accessible inside functions usingcomponent instance(i.e,this
).Composition API: The Composition API uses component logic using imported API functions. The Single FileComponents(SFCs) requires
setup
attribute(<script setup>
) to use imported variables and functions directlyinside template section.
VueJS provides set of directives to show or hide elements based on conditions. The available directives are:v-if,v-else, v-else-if and v-show
1. v-if: The v-if directive adds or removes DOM elements based on the given expression. For example, the belowbutton will not show if isLoggedIn is set to false.
<buttonv-if="isLoggedIn">Logout</button>
You can also control multiple elements with a single v-if statement by wrapping all the elements in a
<template>
element with the condition. For example, you can have both label and button together conditionally applied,<templatev-if="isLoggedIn"><label> Logout</button><button> Logout</button></template>
2. v-else: This directive is used to display content only when the expression adjacent v-if resolves to false.This is similar to else block in any programming language to display alternative content and it is preceded by v-ifor v-else-if block. You don't need to pass any value to this.For example, v-else is used to display LogIn button if isLoggedIn is set to false(not logged in).
<buttonv-if="isLoggedIn"> Logout</button><buttonv-else>LogIn</button>
3. v-else-if: This directive is used when we need more than two options to be checked.For example, we want to display some text instead of LogIn button when ifLoginDisabled property is set to true. Thiscan be achieved through v-else statement.
<buttonv-if="isLoggedIn"> Logout</button><labelv-else-if="isLoginDisabled">Userlogindisabled</label><buttonv-else> Log In</button>
4. v-show: This directive is similar to v-if but it renders all elements to the DOM and then uses the CSSdisplay property to show/hide elements. This directive is recommended if the elements are switched on and offfrequently.
<spanv-show="user.name">Welcome user,{{user.name}}</span>
Below are some of the main differences betweenv-show andv-if directives,
- v-if only renders the element to the DOM if the expression passes whereas v-show renders all elements to the DOMand then uses the CSS display property to show/hide elements based on expression.
- v-if supports v-else and v-else-if directives whereas v-show doesn't support else directives.
- v-if has higher toggle costs while v-show has higher initial render costs. i.e, v-show has a performanceadvantage if the elements are switched on and off frequently, while the v-if has the advantage when it comes toinitial render time.
- v-if supports
<template>
tab but v-show doesn't support.
The built-in v-for directive allows us to loop through items in an array or object. You can iterate on each elementin the array or object.
- Array usage:
<ulid="list"><liv-for="(item, index) in items">{{ index}} -{{item.message}}</li></ul>varvm=newVue({el:'#list',data:{items:[{message:'John'},{message:'Locke'}]}})
You can also use
of
as the delimiter instead ofin
, similar to javascript iterators.- Object usage:
<divid="object"><divv-for="(value, key, index) of user">{{ index}}.{{ key}}:{{ value}}</div></div>varvm=newVue({el:'#object',data:{user:{firstName:'John',lastName:'Locke',age:30}}})
Every Vue application works by creating a new Vue instance with the Vue function. Generally the variable vm (shortfor ViewModel) is used to refer Vue instance. You can create vue instance as below,
varvm=newVue({// options})
As mentioned in the above code snippets, you need to pass options object. You can find the full list of options inthe API reference.
You can achieve conditional group of elements(toggle multiple elements at a time) by applyingv-if directive on
<template>
element which works as invisible wrapper(no rendering) for group of elements.For example, you can conditionally group user details based on valid user condition.
<templatev-if="condition"><h1>Name</h1><p>Address</p><p>Contact Details</p></template>
Vue always tries to render elements as efficient as possible. So it tries to reuse the elements instead of buildingthem from scratch. But this behavior may cause problems in few scenarios.
For example, if you try to render the same input element in both
v-if
andv-else
blocks then it holds theprevious value as below,<templatev-if="loginType === 'Admin'"><label>Admin</label><inputplaceholder="Enter your ID"></template><templatev-else><label>Guest</label><inputplaceholder="Enter your name"></template>
In this case, it shouldn't reuse. We can make both input elements as separate by applyingkey attribute asbelow,
<templatev-if="loginType === 'Admin'"><label>Admin</label><inputplaceholder="Enter your ID"key="admin-id"></template><templatev-else><label>Guest</label><inputplaceholder="Enter your name"key="user-name"></template>
The above code make sure both inputs are independent and doesn't impact each other.
It is recommended not to use v-if on the same element as v-for. Because v-if directive has a higher priority thanv-for.
There are two cases where developers try to use this combination,
- To filter items in a list
For example, if you try to filter the list using v-if tag,
<ul><liv-for="user in users"v-if="user.isActive" :key="user.id">{{user.name}}<li></ul>
This can be avoided by preparing the filtered list using computed property on the initial list
computed:{activeUsers:function(){returnthis.users.filter(function(user){returnuser.isActive})}}......// ......//<ul><liv-for="user in activeUsers" :key="user.id">{{user.name}}<li></ul>
- To avoid rendering a list if it should be hidden
For example, if you try to conditionally check if the user is to be shown or hidden
<ul><liv-for="user in users"v-if="shouldShowUsers" :key="user.id">{{user.name}}<li></ul>
This can be solved by moving the condition to a parent by avoiding this check for each user
<ulv-if="shouldShowUsers"><liv-for="user in users" :key="user.id">{{user.name}}<li></ul>
In order to track each node's identity, and thus reuse and reorder existing elements, you need to provide a unique
key
attribute for each item with inv-for
iteration. An ideal value for key would be the unique id of eachitem.Let us take an example usage,
<divv-for="item in items" :key="item.id">{{item.name}}</div>
Hence, It is always recommended to provide a key with v-for whenever possible, unless the iterated DOM content issimple.
Note: You shouldn't use non-primitive values like objects and arrays as v-for keys. Use string or numericvalues instead.
As the name suggests, mutation methods modifies the original array.
Below are the list of array mutation methods which trigger view updates.
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
If you perform any of the above mutation method on the list then it triggers view update. For example, push methodon array named 'todos' trigger a view update,
vm.todos.push({message:'Baz'})
The methods which do not mutate the original array but always return a new array are called non-mutation methods.
Below are the list of non-mutation methods,
- filter()
- concat()
- slice()
- map()
- reduce()
- find()
- includes()
- every()
- some()
- indexOf()
- join()
For example, lets take a todo list where it replaces the old array with new one based on status filter,
vm.todos=vm.todos.filter(function(todo){returntodo.status.match(/Completed/)})
This approach won't re-render the entire list due to VueJS implementation.
Vue cannot detect changes for the array in the below two cases,
When you directly set an item with the index,For example,
vm.todos[indexOfTodo]=newTodo
When you modify the length of the array, For example,
vm.todos.length=todosLength
You can overcome both the caveats usingset
andsplice
methods, Let's see the solutions with an examples,
First use case solution
// Vue.setVue.set(vm.todos,indexOfTodo,newTodoValue)(or)// Array.prototype.splicevm.todos.splice(indexOfTodo,1,newTodoValue)
Second use case solution
vm.todos.splice(todosLength)
Vue cannot detect changes for the object in property addition or deletion.
Lets take an example of user data changes,
varvm=newVue({data:{user:{name:'John'}}})// `vm.user.name` is now reactivevm.user.email=john@email.com// `vm.user.email` is NOT reactive
You can overcome this scenario using the Vue.set(object, key, value) method or Object.assign(),
Vue.set(vm.user,'email','john@email.com');// (or)vm.user=Object.assign({},vm.user,{email:john@email.com})
You can also use integer type(say 'n') for
v-for
directive which repeats the element many times.<div><spanv-for="n in 20">{{ n}}</span></div>
It displays the number 1 to 20.
Just similar to v-if directive on template, you can also use a
<template>
tag with v-for directive to render ablock of multiple elements.Let's take a todo example,
<ul><templatev-for="todo in todos"><li>{{todo.title}}</li><liclass="divider"></li></template></ul>
You can use event handlers in vue similar to plain javascript. The method calls also support the special $eventvariable.
<buttonv-on:click="show('Welcome to VueJS world', $event)"> Submit</button>methods:{show:function(message,event){// now we have access to the native eventif(event)event.preventDefault()console.log(message);}}
Normally, javascript provides
event.preventDefault() or event.stopPropagation()
inside event handlers. You canuse methods provided by vue, but these methods are meant for data logic instead of dealing with DOM events. Vueprovides below event modifiers for v-on and these modifiers are directive postfixes denoted by a dot.- .stop
- .prevent
- .capture
- .self
- .once
- .passive
Let's take an example of stop modifier,
<!-- the click event's propagation will be stopped --><av-on:click.stop="methodCall"></a>
You can also chain modifiers as below,
<!-- modifiers can be chained --><av-on:click.stop.prevent="doThat"></a>
Vue supports key modifiers on
v-on
for handling keyboard events. Let's take an example of keyup event with enterkeycode.<!-- only call `vm.show()` when the `keyCode` is 13 --><inputv-on:keyup.13="show">
Remembering all the key codes is really difficult. It supports the full list of key codes aliases
- .enter
- .tab
- .delete (captures both "Delete" and "Backspace" keys)
- .esc
- .space
- .up
- .down
- .left
- .right
Now the above keyup code snippet can be written with aliases as follows,
<inputv-on:keyup.enter="submit" /><!-- OR with shorthand notation--><input @keyup.enter="submit" />
Note: The use of keyCode events is deprecated and may not be supported in new browsers.
You can define custom key modifier aliases via the global
config.keyCodes
. There are few guidelines for theproperties- You can't use camelCase. Instead you can use kebab-case with double quotation marks
- You can define multiple values in an array format
Vue.config.keyCodes={f1:112,"media-play-pause":179,down:[40,87]}
Vue supports below modifiers to trigger mouse or keyboard event listeners when the corresponding key is pressed,
- .ctrl
- .alt
- .shift
- .meta
Lets take an example of control modifier with click event,
<!-- Ctrl + Click--><div @click.ctrl="doSomething">Do something</div>
Vue supports below mouse button modifiers
- .left
- .right
- .middle
For example, the usage of
.right
modifier as below<buttonv-if="button==='right'"v-on:mousedown.right="increment"v-on:mousedown.left="decrement" />
You can use the
v-model
directive to create two-way data bindings on form input, textarea, and select elements.Lets take an example of it using input component,
<inputv-model="message" placeholder="Enter input here"><p>The message is: {{ message }}</p>
Remember, v-model will ignore the initial
value
,checked
orselected
attributes found on any form elements.So it always use the Vue instance data as the source of truth.There are three modifiers supported for v-model directive.
1. lazy: By default, v-model syncs the input with the data after each input event. You can add the lazymodifier to instead sync after change events.
<!-- synced after "change" instead of "input"--><inputv-model.lazy="msg" >
2. number: If you want user input to be automatically typecast as a number, you can add the number modifier toyour v-model. Even with type="number", the value of HTML input elements always returns a string. So, this typecastmodifier is required.
<inputv-model.number="age" type="number">
3. trim: If you want whitespace from user input to be trimmed automatically, you can add the trim modifier toyour v-model.
<inputv-model.trim="msg">
Components are reusable Vue instances with a name. They accept the same options as new Vue, such as data, computed,watch, methods, and lifecycle hooks(except few root-specific options like el).
Lets take an example of counter component,
// Define a new component called button-counterVue.component('button-counter',{template:'<button v-on:click="count++">You clicked me {{ count }} times.</button>'data:function(){return{count:0}},})
Let's use this component inside a root Vue instance created with new Vue
<divid="app"><button-counter></button-counter></div>varvm=newVue({el:'#app'});
Props are custom attributes you can register on a component. When a value is passed to a prop attribute, it becomesa property on that component instance. You can pass those list of values as props option and use them as similar todata variables in template.
Vue.component('todo-item',{props:['title'],template:'<h2>{{ title }}</h2>'})
Once the props are registered, you can pass them as custom attributes.
<todo-item title="Learn Vue conceptsnfirst"></todo-item>
In VueJS 2.x, every component must have a single root elementwhen template has more than one element. In thiscase, you need to wrap the elements with a parent element.
<template> <divclass="todo-item"> <h2>{{ title }}</h2> <divv-html="content"></div> </div></template>
Otherwise there will an error throwing, saying that "Component template should contain exactly one rootelement...".
Whereas in 3.x, components now can have multiple root nodes. This way of adding multiple root nodes is called asfragments.
<template> <h2>{{ title }}</h2> <divv-html="content"></div></template>
If you want child wants to communicate back up to the parent, then emit an event from child using
$emit
object toparent,Vue.component('todo-item',{props:['todo'],template:` <div> <h3>{{ todo.title }}</h3> <button v-on:click="$emit('increment-count', 1)"> Add </button> <div v-html="todo.description"></div> </div> `})
Now you can use this todo-item in parent component to access the count value.
<ulv-for="todointodos"> <li> <todo-item v-bind:key="todo.id" v-bind:todo="todo" v-on:increment-count="total += 1" /></todo-item> </li></ul><span> Total todos count is {{total}}</span>
The custom events can also be used to create custom inputs that work with v-model. The
<input>
inside thecomponent must follow below rules,Bind the value attribute to a value prop
On input, emit its own custom input event with the new value.
Let's take a custom-input component as an example,
Vue.component('custom-input',{props:['value'],template:` <input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" /> `})
Now you can usev-model
with this component,
<custom-inputv-model="searchInput"></custom-input>
Vue implements a content distribution API using the element to serve as distribution outlets for contentcreated after the current Web Components spec draft.
Let's create an alert component with slots for content insertion,
Vue.component('alert',{template:` <div> <strong>Error!</strong> <slot></slot> </div> `})
Now you can insert dynamic content as below,
<alert> There is an issue with in application.</alert>
The components which are globally registered can be used in the template of any root Vue instance (new Vue) createdafter registration.
In the global registration, the components created using Vue.component as below,
Vue.component('my-component-name',{// ... options ...})
Let's take multiple components which are globally registered in the vue instance,
Vue.component('component-a',{/* ... */})Vue.component('component-b',{/* ... */})Vue.component('component-c',{/* ... */})newVue({el:'#app'})
The above components can be used in the vue instance,
<div> <component-a></component-a> <component-b></component-b> <component-c></component-c></div>
Remember that the components can be used in subcomponents as well.
Due to global registration, even if you don't use the component it could still be included in your final build. Soit will create unnecessary javascript in the application. This can be avoided using local registration with thebelow steps,
- First you need to define your components as plain JavaScript objects
varComponentA={/* ... */}varComponentB={/* ... */}varComponentC={/* ... */}
Locally registered components will not be available in sub components. In this case, you need to add them incomponents section
varComponentA={/* ... */}varComponentB={components:{'component-a':ComponentA},// ...}
- First you need to define your components as plain JavaScript objects
2. You can use the components in the components section of the vue instance, ```javascript new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } }) ``` **[⬆ Back to Top](#table-of-contents)**
Inlocal registration, you need to create each component in components folder(optional but it is recommended)and import them in another component file components section.
Let's say you want to register component A and B in component C, the configuration seems as below,
importComponentAfrom'./ComponentA'importComponentBfrom'./ComponentB'exportdefault{components:{ ComponentA, ComponentB},// ...}
Now both ComponentA and ComponentB can be used inside ComponentC's template.
Inglobal registration, you need to export all common or base components in a separate file. But some of thepopular bundlers like
webpack
make this process simpler by usingrequire.context
to globally register basecomponents in the below entry file(one-time).importVuefrom'vue'importupperFirstfrom'lodash/upperFirst'importcamelCasefrom'lodash/camelCase'constrequireComponent=require.context(// The relative path of the components folder'./components',// Whether or not to look in subfoldersfalse,// The regular expression used to match base component filenames/Base[A-Z]\w+\.(vue|js)$/)requireComponent.keys().forEach(fileName=>{// Get component configconstcomponentConfig=requireComponent(fileName)// Get PascalCase name of componentconstcomponentName=upperFirst(camelCase(// Strip the leading `./` and extension from the filenamefileName.replace(/^\.\/(.*)\.\w+$/,'$1')))// Register component globallyVue.component(componentName,// Look for the component options on `.default`, which will// exist if the component was exported with `export default`,// otherwise fall back to module's root.componentConfig.default||componentConfig)})
You can declare props with type or without type. But it is recommended to have prop types because it provides thedocumentation for the component and warns the developer for any incorrect data type being assigned.
props:{name:String,age:Number,isAuthenticated:Boolean,phoneNumbers:Array,address:Object}
As mentioned in the above code snippet, you can list props as an object, where the properties' names and valuescontain the prop names and types, respectively.
All props follows a one-way-down binding between the child property and the parent one. i.e, When the parentproperty is updated then that latest prop value will be passed down to the child, but not the otherway(child toparent) around. The child component should not mutate the prop otherwise it throws a warning in the console.The possible mutation cases can be solved as below,
- When you try to use parent prop as initial value for child property:
In this case you can define a local property in child component and assign parent value as initial value
props:['defaultUser'],data:function(){return{username:this.defaultUser}}
- When you try to transform the parent prop:
You can define a computed property using the prop's value,
props:['environment'],computed:{localEnvironment:function(){returnthis.environment.trim().toUpperCase()}}
A non-prop attribute is an attribute that is passed to a component, but does not have a corresponding prop defined.
For example, If you are using a 3rd-party custom-input component that requires a
data-tooltip
attribute on theinput then you can add this attribute to component instance,<custom-input data-tooltip="Enter your input" />
If you try to pass the props from parent component the child props with the same names will be overridden. Butprops like
class
andstyle
are exception to this, these values will be merged in the child component.<!-- Child component--><input type="date"><!-- Parent component --><custom-input />
Vue provides validations such as types, required fields, default values along with customized validations. You canprovide an object with validation requirements to the value of props as below,
Let's take an example of user profile Vue component with possible validations,
Vue.component('user-profile',{props:{// Basic type check (`null` matches any type)age:Number,// Multiple possible typesidentityNumber:[String,Number],// Required stringemail:{type:String,required:true},// Number with a default valueminBalance:{type:Number,default:10000},// Object with a default valuemessage:{type:Object,// Object or array defaults must be returned from// a factory functiondefault:function(){return{message:'Welcome to Vue'}}},// Custom validator functionlocation:{validator:function(value){// The value must match one of these stringsreturn['India','Singapore','Australia'].indexOf(value)!==-1}}}})
The v-model directive on a component usesvalue as the prop andinput as the event, but some input typessuch as
checkboxes
andradio buttons
may need to use the value attribute for a server side value. In this case,it is preferred to customize model directive.Let's take an example of checkbox component,
Vue.component('custom-checkbox',{model:{prop:'checked',event:'change'},props:{checked:Boolean},template:` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > `})
Now you can use v-model on this customized component as below,
<custom-checkboxv-model="selectFramework"></custom-checkbox>
The selectFramework property will be passed to the checked prop and same property will be updated when customcheckbox component emits a change event with a new value.
There are many ways Vue provides transition effects when items are inserted, updated, or removed from the DOM.
Below are the possible ways,
- Automatically apply classes for CSS transitions and animations
- Integrate 3rd-party CSS animation libraries. For example, Animate.css
- Use JavaScript to directly manipulate the DOM during transition hooks
- Integrate 3rd-party JavaScript animation libraries. For example, Velocity.js
Vue Router is a official routing library for single-page applications designed for use with the Vue.js framework.
Below are their features,
- Nested route/view mapping
- Modular, component-based router configuration
- Route params, query, wildcards
- View transition effects powered by Vue.js' transition system
- Fine-grained navigation control
- Links with automatic active CSS classes
- HTML5 history mode or hash mode, with auto-fallback in IE9
- Restore scroll position when going back in history mode
It is easy to integrate vue router in the vue application.
Let us see the example with step by step instructions.
Step 1: Configure router link and router view in the template
<script src="https://unpkg.com/vue/dist/vue.js"></script><script src="https://unpkg.com/vue-router/dist/vue-router.js"></script><div> <h1>Welcome to Vue routing app!</h1> <p> <!-- use router-link component for navigation using `to` prop. It rendered as an `<a>` tag --> <router-link to="/home">Home</router-link> <router-link to="/services">Services</router-link> </p> <!-- route outlet in which component matched by the route will render here --> <router-view></router-view></div>
Step 2: Import Vue and VueRouter packages and then apply router
importVuefrom'vue';importVueRouterfrom'vue-router';Vue.use(VueRouter)
Step 3: Define or import route components.
constHome={template:'<div>Home</div>'}constServices={template:'<div>Services</div>'}
Step 4: Define your route where each one maps to a component
constroutes=[{path:'/home',component:Home},{path:'/services',component:Services}]
Step 5: Create the router instance and pass the
routes
optionconstrouter=newVueRouter({ routes// short for `routes: routes`})
Step 6: Create and mount the root instance.
constapp=newVue({ router}).$mount('#app')
Now you are able to navigate different pages(Home, Services) with in Vue application.
Sometimes it may be required to map routes to the same component based on a pattern.
Let's take a user component with the mapped URLs like
/user/john/post/123
and/user/jack/post/235
using dynamicsegments,constUser={template:'<div>User {{ $route.params.name }}, PostId: {{ $route.params.postid }}</div>'}constrouter=newVueRouter({routes:[// dynamic segments start with a colon{path:'/user/:name/post/:postid',component:User}]})
When you navigate from one URL to other(mapped with a single component) using routes with params then the samecomponent instance will be reused. Even though it is more efficient than destroying the old instance and thencreating a new one, the lifecycle hooks of the component will not be called.
This problem can be solved using either of the below approaches,
Watch the $route object:
constUser={template:'<div>User {{ $route.params.name }} </div>',watch:{'$route'(to,from){// react to route changes...}}}
Use beforeRouteUpdate navigation guard: This is only available since 2.2 version.
constUser={template:'<div>User {{ $route.params.name }} </div>',beforeRouteUpdate(to,from,next){// react to route changes and then call next()}}
Note that the beforeRouteEnter guard does NOT have access tothis
. Instead you can pass a callback tonext
toaccess the vm instance.
Sometimes the URL might be matched by multiple routes and the confusion of which route need to be mapped isresolved by route matching priority. The priority is based on order of routes configuration. i.e, The route whichdeclared first has higher priority.
constrouter=newVueRouter({routes:[// dynamic segments start with a colon{path:'/user/:name',component:User}// This route gets higher priority{path:'/user/:name',component:Admin}{path:'/user/:name',component:Customer}]})
Generally, the app is composed of nested components which are nested multiple levels deep. The segments of a URLcorresponds to a certain structure of these nested components. To render components into the nested outlet, youneed to use the
children
option inVueRouter
constructor config.Let's take a user app composed of profile and posts nested components with respective routes. You can also define adefault route configuration when there is no matching nested route.
constrouter=newVueRouter({routes:[{path:'/user/:id',component:User,children:[{// UserProfile will be rendered inside User's <router-view> when /user/:id/profile is matchedpath:'profile',component:UserProfile},{// UserPosts will be rendered inside User's <router-view> when /user/:id/posts is matchedpath:'posts',component:UserPosts},// UserHome will be rendered inside User's <router-view> when /user/:id is matched{path:'',component:UserHome},]}]})
Single File Components are an easy concept to understand. Earlier you might heard about all three parts(HTML,JavaScript and CSS) of your application kept in different components. But Single File Components encapsulate thestructure, styling and behaviour into one file. In the beginning, it seems strange to have all three parts in onefile, but it actually makes a lot more sense.
Let's take an example of Singile File Components
<template> <div> <h1>Welcome {{ name }}!</h1> </div></template><script>module.exports= {data:function() {return { name:'John' } }}</script><style scoped>h1 {color:#34c779;padding:3px;}</style>
As for the latest modern UI development, separation of concerns is not equal to separation of file types. So it ispreferred to divide codebase layers into loosely-coupled components and compose them instead of dividing thecodebase into three huge layers that interweave with one another. This way makes Single File Components morecohesive and maintainable by combining template, logic and styles together inside a component.You can also still maintain javascript and CSS files separately with hot-reloading and pre-compilation features.
For example,
<template> <div>This section will be pre-compiled and hot reloaded</div></template><script src="./my-component.js"></script><style src="./my-component.css"></style>
The Single File Components solve the common problems occurred in a javascript driven application with a .vueextension. The list of issues are,
- Global definitions force unique names for every component
- String templates lack syntax highlighting and require ugly slashes for multiline HTML
- No CSS support means that while HTML and JavaScript are modularized into components, CSS is conspicuously leftout
- No build step restricts us to HTML and ES5 JavaScript, rather than preprocessors like Pug (formerly Jade) andBabel.
Filters can be used to apply common text formatting. These Filters should be appended to the end of the JavaScriptexpression, denoted by the "pipe" symbol. You can use them in two specific cases:
- mustache interpolations
- v-bind expressions
For example, Let's define a local filter named capitalize in a component's options
filters:{capitalize:function(value){if(!value)return''value=value.toString()returnvalue.charAt(0).toUpperCase()+value.slice(1)}}
Now you can use the filter in either mustache interpolation or v-bind expression,
<!-- in mustaches-->{{ username | capitalize }}<!-- in v-bind--><divv-bind:id="username|capitalize"></div>
You can define filters in two ways,
- Local filters:You can define local filters in a component's options. In this case, filter is applicable to that specificcomponent.
filters:{capitalize:function(value){if(!value)return''value=value.toString()returnvalue.charAt(0).toUpperCase()+value.slice(1)}}
2. **Global filters:** You can also define a filter globally before creating the Vue instance. In this case, filter is applicable to all the components with in the vue instance, ```javascript Vue.filter('capitalize', function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) }) new Vue({ // ... }) ``` **[⬆ Back to Top](#table-of-contents)**
You can chain filters one after the other to perform multiple manipulations on the expression. The genericstructure of filter chain would be as below,
{{ message | filterA | filterB | filterB ... }}
In the above chain stack, you can observe that message expression applied with three filters, each separated by apipe(|) symbol. The first filter(filterA) takes the expression as a single argument and the result of theexpression becomes an argument for second filter(filterB) and the chain continue for remaining filters.
For example, if you want to transform date expression with a full date format and uppercase then you can applydateFormat and uppercase filters as below,
{{ birthday | dateFormat | uppercase }}
Yes, you can pass arguments for a filter similar to a javascript function. The generic structure of filterparameters would be as follows,
{{ message | filterA('arg1', arg2) }}
In this case, filterA takes message expression as first argument and the explicit parameters mentioned in thefilter as second and third arguments.
For example, you can find the exponential strength of a particular value
{{ 2 | exponentialStrength(10) }}<!-- prints 2 power 10 = 1024-->
Plugins provides global-level functionality to Vue application. The plugins provide various services,
- Add some global methods or properties. For example, vue-custom-element
- Add one or more global assets (directives, filters and transitions). For example, vue-touch
- Add some component options by global mixin. For example, vue-router
- Add some Vue instance methods by attaching them to Vue.prototype.
- A library that provides an API of its own, while at the same time injecting some combination of the above. Forexample, vue-router
The Plugin is created by exposing an
install
method which takes Vue constructor as a first argument along withoptions. The structure of VueJS plugin with possible functionality would be as follows,MyPlugin.install=function(Vue,options){// 1. add global method or propertyVue.myGlobalMethod=function(){// some logic ...}// 2. add a global assetVue.directive('my-directive',{bind(el,binding,vnode,oldVnode){// some logic ...}// ...})// 3. inject some component optionsVue.mixin({created:function(){// some logic ...}// ...})// 4. add an instance methodVue.prototype.$myMethod=function(methodOptions){// some logic ...}}
You can use plugin by passing your plugin to Vue'suse global method. You need to apply this method beforestart your app by calling new Vue().
// calls `MyPlugin.install(Vue, { someOption: true })`Vue.use(MyPlugin)newVue({//... options})
Mixin gives us a way to distribute reusable functionalities in Vue components. These reusable functions are mergedwith existing functions. A mixin object can contain any component options. Let us take an example of mixin with
created
lifecycle which can be shared across components,constmyMixin={created(){console.log("Welcome to Mixins!")}}varapp=newVue({el:'#root',mixins:[myMixin]})
Note: Multiple mixins can be specified in the mixin array of the component.
Sometimes there is a need to extend the functionality of Vue or apply an option to all Vue components available inour application. In this case, mixins can be applied globally to affect all components in Vue. These mixins arecalled as global mixins.
Let's take an example of global mixin,
Vue.mixin({created(){console.log("Write global mixins")}})newVue({el:'#app'})
In the above global mixin, the mixin options spread across all components with the console running during theinstance creation. These are useful during test, and debugging or third party libraries. At the same time, You needto use these global mixins sparsely and carefully, because it affects every single Vue instance created, includingthird party components.
Using Vue CLI, mixins can be specified anywhere in the project folder but preferably within
/src/mixins
for easeof access. Once these mixins are created in a.js
file and exposed with theexport
keyword, they can beimported in any component with theimport
keyword and their file paths.When a mixin and the component itself contain overlapping options, the options will be merged based on somestrategies.
- The data objects undergo a recursive merge, with the component's data taking priority over mixins in cases ofoverlapping or conflicts.
varmixin={data:function(){return{message:'Hello, this is a Mixin'}}}newVue({mixins:[mixin],data:function(){return{message:'Hello, this is a Component'}},created:function(){console.log(this.$data);// => { message: "Hello, this is a Component'" }}})
- The data objects undergo a recursive merge, with the component's data taking priority over mixins in cases ofoverlapping or conflicts.
2. The Hook functions which are overlapping merged into an array so that all of them will be called. Mixin hooks will be called before the component's own hooks. ```javascript const myMixin = { created(){ console.log("Called from Mixin") } } new Vue({ el: '#root', mixins: [myMixin], created(){ console.log("Called from Component") } }) // Called from Mixin // Called from Component ```3. The options that expect object values(such as methods, components and directives) will be merged into the same object. In this case, the component's options will take priority when there are conflicting keys in these objects. ```javascript var mixin = { methods: { firstName: function () { console.log('John') }, contact: function () { console.log('+65 99898987') } } } var vm = new Vue({ mixins: [mixin], methods: { lastName: function () { console.log('Murray') }, contact: function () { console.log('+91 893839389') } } }) vm.firstName() // "John" vm.lastName() // "Murray" vm.contact() // "+91 893839389" ``` **[⬆ Back to Top](#table-of-contents)**
Vue uses the default strategy which overwrites the existing value while custom options are merged. But if you wanta custom option merged using custom login then you need to attach a function to
Vue.config.optionMergeStrategies
For the example, the structure of
myOptions
custom option would be as below,Vue.config.optionMergeStrategies.myOption=function(toVal,fromVal){// return mergedVal}
Let's take below Vuex 1.0 merging strategy as an advanced example,
constmerge=Vue.config.optionMergeStrategies.computedVue.config.optionMergeStrategies.vuex=function(toVal,fromVal){if(!toVal)returnfromValif(!fromVal)returntoValreturn{getters:merge(toVal.getters,fromVal.getters),state:merge(toVal.state,fromVal.state),actions:merge(toVal.actions,fromVal.actions)}}
Custom Directives are tiny commands that you can attach to DOM elements. They are prefixed with v- to let thelibrary know you're using a special bit of markup and to keep syntax consistent. They are typically useful if youneed low-level access to an HTML element to control a bit of behavior.
Let's create a custom focus directive to provide focus on specific form element during page load time,
// Register a global custom directive called `v-focus`Vue.directive('focus',{// When the bound element is inserted into the DOM...inserted:function(el){// Focus the elementel.focus()}})
Now you can use v-focus directive on any element as below,
<inputv-focus>
You can also register directives locally(apart from globally) using directives option in component as below,
directives:{focus:{// directive definitioninserted:function(el){el.focus()}}}
Now you can use v-focus directive on any element as below,
<inputv-focus>
A directive object can provide several hook functions,
- bind: This occurs once the directive is attached to the element.
- inserted: This hook occurs once the element is inserted into the parent DOM.
- update: This hook is called when the element updates, but children haven't been updated yet.
- componentUpdated: This hook is called once the component and the children have been updated.
- unbind: This hook is called only once when the directive is removed.
Note: There are several arguments that can be passed to the above hooks.
All the hooks have
el
,binding
, andvnode
as arguments. Along with that,update andcomponentUpdatedhooks exposeoldVnode
, to differentiate between the older value passed and the newer value. Below are thearguments passed to the hooks,el
: The element the directive is bound to and it can be used to directly manipulate the DOM.binding
: An object containing the following properties.1.name
: The name of the directive, without thev-
prefix.2.value
: The value passed to the directive. For example inv-my-directive="1 + 1"
, the value would be 2.3.oldValue
: The previous value, only available in update and componentUpdated. It is available whether or notthe value has changed.4.expression
: The expression of the binding as a string. For example inv-my-directive="1 + 1"
, theexpression would be "1 + 1".5.arg
: The argument passed to the directive, if any. For example in v-my-directive:foo, the arg would be "foo".6.modifiers
: An object containing modifiers, if any. For example in v-my-directive.foo.bar, the modifiersobject would be{ foo: true, bar: true }
.vnode
: The virtual node produced by Vue's compiler.oldVnode
: The previous virtual node, only available in the update and componentUpdated hooks.
The arguments can be represented diagrammatically across the hooks as below,
A directive can take any valid javascript expression. So if you want to pass multiple values then you can pass in aJavaScript object literal.
Let's pass object literal to an avatar directive as below
<divv-avatar="{ width:500, height:400, url:'path/logo', text:'Iron Man' }"></div>
Now let us configure avatar directive globally,
Vue.directive('avatar',function(el,binding){console.log(binding.value.width)// 500console.log(binding.value.height)// 400console.log(binding.value.url)// path/logoconsole.log(binding.value.text)// "Iron Man"})
In few cases, you may want the same behavior on
bind
andupdate
hooks irrespective of other hooks. In thissituation you can use function shorthand,Vue.directive('theme-switcher',function(el,binding){el.style.backgroundColor=binding.value})
In VueJS, the templates are very powerful and recommended to build HTML as part of your application. However, someof the special cases like dynamic component creation based on input or slot value can be achieved through renderfunctions. Also, these functions gives the full programmatic power of javascript eco system.
Render function is a normal function which receives a
createElement
method as it's first argument used to createvirtual nodes. Internally Vue.js' templates actually compile down to render functions at build time. Hencetemplates are just syntactic sugar of render functions.Let's take an example of simple Div markup and corresponding render function.The HTML markup can be written in template tag as below,
<template> <div:class="{'is-rounded': isRounded}"> <p>Welcome to Vue render functions</p> </div></template>
and the compiled down or explicit render function would appear as below,
render:function(createElement){returncreateElement('div',{'class':{'is-rounded':this.isRounded}},[createElement('p','Welcome to Vue render functions')]);}
Note: The react components are built with render functions in JSX.
The createElement accepts few arguments to use all the template features.
Let us see the basic structure of createElement with possible arguments,
//@returns {VNode}createElement(// An HTML tag name, component options, or async function resolving to one of these.// Type is {String | Object | Function}// Required.'div',// A data object corresponding to the attributes you would use in a template.// Type is {Object}// Optional.{// Normal HTML attributesattrs:{id:'someId'},// Component propsprops:{myProp:'somePropValue'},// DOM propertiesdomProps:{innerHTML:'This is some text'},// Event handlers are nested under `on`on:{click:this.clickHandler},// Similar to `v-bind:style`, accepting either a string, object, or array of objects.style:{color:'red',fontSize:'14px'},// Similar to `v-bind:class`, accepting either a string, object, or array of strings and objects.class:{className1:true,className2:false}// ....},// Children VNodes, built using `createElement()`, or using strings to get 'text VNodes'.// Type is {String | Array}// Optional.['Learn about createElement arguments.',createElement('h1','Headline as a child virtual node'),createElement(MyComponent,{props:{someProp:'This is a prop value'}})])
See details of the date object inofficialdoc.
All virtual nodes(VNodes) in the component tree must be unique.i.e, You can't write duplicated nodes in astraightforward way. If you want to duplicate the same element/component many times then you should use factoryfunction.
The below render function is invalid where you are trying to duplicate h1 element 3 times,
render:function(createElement){varmyHeadingVNode=createElement('h1','This is a Virtual Node')returncreateElement('div',[myHeadingVNode,myHeadingVNode,myHeadingVNode])}
You can make duplicates with factory function,
render:function(createElement){returncreateElement('div',Array.apply(null,{length:3}).map(function(){returncreateElement('h1','This is a Virtual Node')}))}
VueJS provides proprietary alternatives and plain javascript usage for the template features.
Let's list down them in a table for comparison,
Templates Render function Conditional and looping directives: v-if and v-for Use JavaScript's if/else and map concepts Two-way binding: v-model Apply own JS logic with value binding and event binding Capture Event modifiers: .passive, .capture, .once and .capture.once or .once.capture &, !, ~ and ~! Event and key modifiers: .stop, .prevent, .self, keys(.enter, .13) and Modifiers Keys(.ctrl, .alt, .shift, .meta) Use javascript solutions: event.stopPropagation(), event.preventDefault(), if (event.target !== event.currentTarget) return, if (event.keyCode !== 13) return and if (!event.ctrlKey) return Slots: slot attributes Render functions provide this.$slots and this.$scopedSlots instance properties The functional components are just simple functions to create simple components just by passing a context. Everyfunctional component follows two rules,
- Stateless: It doesn't keep any state by itself
- Instanceless: It has no instance, thus no this
You need to define
functional: true
to make it functional. Let's take an example of functional components,Vue.component('my-component',{functional:true,// Props are optionalprops:{// ...},// To compensate for the lack of an instance,// we are now provided a 2nd context argument.render:function(createElement,context){// ...}})
Note: The functional components are quite popular in React community too.
Even though ReactJS and VueJS are two different frameworks there are few similarities(apart from the common goal ofutilized in interface design) between them.
- Both frameworks are based on theVirtual DOM model
- They provide features such Component-based structure and reactivity
- They are intended for working with the root library, while all the additional tasks are transferred to otherlibraries(routing, state management etc).
Even though VueJS and ReactJS share few common features there are many difference between them.
Let's list down them in a table format.
Feature VueJS ReactJS Type JavaScript MVVM Framework JavaScript Library Platform Primarily focused on web development Both Web and Native Learning Curve Easy to learn the framework A steep learning curve and requires deep knowledge Simplicity Vue is simpler than React React is more complex than Vue Bootstrap Application Vue-cli CRA (Create React App) Vue has the following advantages over React
- Vue is smaller and faster
- The convenient templates ease the process of developing
- It has simpler javascript syntax without learning JSX
React has the following advantages over Vue
- ReactJS gives more flexibility in large apps developing
- Easy to test
- Well-suited for mobile apps creation
- The eco system is quite big and well matured.
The the syntax of Vue and Angular is common at some points because Angular is the basis for VueJS development inthe beginning.
But there are many differences between VueJS and Angular as listed,
Feature VueJS Angular Complexity Easy to learn, simple API and design The framework is bit huge and need some learning curve on typescript etc Binding of Data One-way binding Two-way binding Learning Curve Easy to learn the framework A steep learning curve and requires deep knowledge Founders Created by Former Google Employee Powered by Google Initial Release February 2014 September 2016 Model Based on Virtual DOM(Document Object Model) Based on MVC(Model-View-Controller) The dynamic component will allow you to dynamically switch between multiple components without updating the routeof the application and even retaining the state of the component when switching back to the initial component. Itworks by using
<component>
tag withv-bind:is
attribute which accept dynamic component.Let's create a dynamic component vue instance to switch between different pages of a website,
newVue({el:'#app',data:{currentPage:'home'},components:{home:{template:"<p>Home</p>"},about:{template:"<p>About</p>"},contact:{template:"<p>Contact</p>"}}})
Now you can use the dynamic component which holds the current page,
<divid="app"><componentv-bind:is="currentPage"><!-- component changes when currentPage changes! --><!-- output: Home --></component></div>
The component will be unmounted when it is switched away but it is possible to force the inactive component aliveusing
<keep-alive>
componentNote: The value of
:is
attribute can be either name of the registered component or the actual imported objectitself.Keep-alive tag is an abstract component used to preserve component state or avoid re-rendering. When you wrapped <keep-alive> tag around a dynamic component, it caches the inactive component instances without destroying them.
Let's see the example usage of it,
<!-- Inactive components will be cached! --><keep-alive><componentv-bind:is="currentTabComponent"></component></keep-alive>
When there are multiple conditional children, it requires that only one child is rendered at a time.
<!-- multiple conditional children --><keep-alive><comp-av-if="a > 1"></comp-a><comp-bv-else></comp-b></keep-alive>
Note: Remember that keep-alive tag doesn't render a DOM element itself, and doesn't show up in the componentparent chain.
In large applications, we may need to divide the app into smaller chunks and only load a component from the serverwhen it's needed. To make this happen, Vue allows you to define your component as a factory function thatasynchronously resolves your component definition. These components are known as async component.
Let's see an example of async component using webpack code-splitting feature,
Vue.component('async-webpack-example',function(resolve,reject){// Webpack automatically split your built code into bundles which are loaded over Ajax requests.require(['./my-async-component'],resolve)})
Vue will only trigger the factory function when the component needs to be rendered and will cache the result forfuture re-renders.
Async component factory is useful to resolve the component asynchronously. The async component factory can returnan object of the below format.
constAsyncComponent=()=>({// The component to load (should be a Promise)component:import('./MyComponent.vue'),// A component to use while the async component is loadingloading:LoadingComponent,// A component to use if the load failserror:ErrorComponent,// Delay before showing the loading component. Default: 200ms.delay:200,// The error component will be displayed if a timeout is// provided and exceeded. Default: Infinity.timeout:3000})
If you keep an
inline-template
on a child component then it will use its inner content as a template instead oftreating as reusable independent content.<my-componentinline-template><div><h1>Inline templates</h1><p>Treated as component component owne content</p></div></my-component>
Note: Even though this inline-templates gives more flexibility for template authoring, it is recommended todefine template using template property or tag inside .vue component.
Apart from regular templates and inline templates, you can also define templates using a script element with thetype
text/x-template
and then referencing the template by an id.Let's create a x-template for simple use case as below,
<scripttype="text/x-template"id="script-template"><p>Welcome to X-Template feature</p></script>
Now you can define the template using reference id,
Vue.component('x-template-example',{template:'#script-template'})
The Components that can recursively invoke themselves in their own template are known as recursive components.
Vue.component('recursive-component',{template:`<!--Invoking myself!--> <recursive-component></recursive-component>`});
Recursive components are useful for displaying comments on a blog, nested menus, or basically anything where theparent and child are the same, eventhough with different content.
Note: Remember that recursive component can lead infinite loops with
max stack size exceeded
error, so makesure recursive invocation is conditional(for example, v-if directive).In complex applications, vue components will actually be each other's descendent and ancestor in the render tree.
Let's say componentA and componentB included in their respective templates which makes circular dependency,
//ComponentA<div><component-b></div>
//ComponentB<div><component-a></div>
This can be solved by either registering(or wait until) the child component in
beforeCreate
hook or usingwebpack's asynchronous import while registering the component,Solution1:
beforeCreate:function(){this.$options.components.componentB=require('./component-b.vue').default}
Solution2:
components:{componentB:()=>import('./component-b.vue')}
Some environments(Google Chrome Apps) prohibits the usage of
new Function()
for evaluating expressions and thefull builds of vue applications depends on this feature to compile templates. Due to this reason, the full buildsof VueJS application are not CSP complaint.In this case you can useruntime-only builds with Webpack + vue-loader or Browserify + vueify technology stackthrough which templates will be precompiled into render functions. This way you can make sure VueJS applicationsare 100% CSP complaint.
There are two types of builds provided by VueJS,
1. Full: These are the builds that contain both the compiler and the runtime.
2. Runtime Only: These builds doesn't include compiler but the code is responsible for creating Vue instances,rendering and patching virtual DOM. These are about 6KB lighter min+gzip.
Below are the list of different builds of VueJS based on type of build,
Type UMD CommonJS ES Module (for bundlers) ES Module (for browsers) Full vue.js vue.common.js vue.esm.js vue.esm.browser.js Runtime only vue.runtime.js vue.runtime.common.js vue.runtime.esm.js NA Full (production) vue.min.js NA NA vue.esm.browser.min.js Runtime-only (production) vue.runtime.min.js NA NA NA You can configure vueJS in webpack using alias as below,
module.exports={// ...resolve:{alias:{'vue$':'vue/dist/vue.esm.js'// 'vue/dist/vue.common.js' for webpack 1}}}
The compiler is is responsible for compiling template strings into JavaScript render functions.
For example, the below code snippet shows the difference of templates which need compiler and not,
// this requires the compilernewVue({template:'<div>{{ message }}</div>'})// this does notnewVue({render(h){returnh('div',this.message)}})
DevTools is a browser extension allowing you to inspect and debug your Vue applications in a more user-friendlyinterface. You can find the below extensions for different browsers or environments,
- Chrome Extension
- Firefox Addon
- Standalone Electron app (works with any environment)
The DevTools plugins can be used as shown in the below snapshot,
Note:
- If the page uses a production/minified build of Vue.js, devtools inspection is disabled by default so the Vuepane won't show up.
- To make it work for pages opened via
file://
protocol, you need to check "Allow access to file URLs" for thisextension in Chrome's extension management panel.
It supports all ECMAScript5 complaint browsers as mentioned in thisurl. VueJSdoesn't support IE8 browser and below, because it uses ECMAScript 5 features that are un-shimmable(require supportfrom the underlying JS engine) in IE8.
VueJS is available in jsdelivr, unpkg and cdnjs etc CDNs. Normally you can use them for prototyping or learningpurposes.
For example, you can use them using jsdelivr with latest versions as below,
<scriptsrc="https://cdn.jsdelivr.net/npm/vue@2.6.7/dist/vue.js"></script>
You can use it for native ES modules as below,
<scripttype="module"> import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.7/dist/vue.esm.browser.js'</script>
Note: You can remove version number to get latest version.
It is extremely rare situation of having to manually force an update despite the fact that no reactive data haschanged. i.e, To force the Vue instance to re-render manually. You can do it force update using **vm.$forceUpdate()** API method.
Note: It does not affect all child components but only the instance itself and child components with insertedslot content.
If you want to render
a lot of static content
then you need to make sure it only evaluated once and then cachedthereafter. In this case, you can usev-once
directive by wrapping at the root level.The example usage of v-once directive would be as below,
Vue.component('legal-terms',{template:` <div v-once> <h1>Legal Terms</h1> ... a lot of static content goes here... </div> `})
Note: It is recommended not to overuse unless there is slow rendering due to lot of static content.
The root instance(new Vue()) can be accessed with the
$root
property.Let's see the usage of root instance with an example.
First let's create a root instance with properties and methods as below,
// The root Vue instancenewVue({data:{age:26},computed:{fullName:function(){/* ... */}},methods:{interest:function(){/* ... */}}})
Now you can access root instance data and it's methods with in subcomponents as below,
// Get root datathis.$root.age// Set root datathis.$root.age=29// Access root computed propertiesthis.$root.fullName// Call root methodsthis.$root.interest()
It is recommend using Vuex to manage state instead of using root instance as a global store.
Below are the top 10 organizations using VueJS for their applications or products,
- Facebook - Used on marketing side of its Newsfeed
- Netflix - Used in two internal apps for building movie streaming interfaces
- Adobe - Used for Portfolio, a custom website builder designed to help users showcase their creative work
- Xiaomi - Used for products where it sells from consumer electronics to software
- Alibaba - Provide their apps an excellent experience to its customers
- WizzAir - A budget airline WizzAir used for their customers user interface
- EuroNews
- Laracasts
- GitLab
- BMW
When the default render function encounters an error then you can use rennderError as an alternative render output.The error will be passed to renderError as the second argument.
The example usage of renderError is as below,
newVue({render(h){thrownewError('An error')},renderError(h,err){returnh('div',{style:{color:'red'}},err.stack)}}).$mount('#app')
The $parent object refers to theimmediate outer scope. The parent will be accessible as
this.$parent
for the child, and the child will be pushed into the parent's $children array. It establishes aparent-child relationship between the two instances(parent and child). You can access parent data and propertiessimilar to $root.Vuex is a state management pattern + library (Flux-inspired Application Architecture) for Vue.js applications. Itserves as a centralized store for all the components in an application, with rules ensuring that the state can onlybe mutated in a predictable fashion.
The state management has state, view and actions as major components. The pattern followed by these components in aapplication is known as State Management Pattern. Below are the components in a detail,
- Thestate, which is the source of truth that drives our app
- Theview, which is just a declarative mapping of the state
- Theactions, which are the possible ways the state could change in reaction to user inputs from the view.
Let us take a counter example which follows state management pattern with the above 3 components,
newVue({// statedata(){return{count:0}},// viewtemplate:` <div>{{ count }}</div> `,// actionsmethods:{increment(){this.count++}}})
Vue.js has a one-way data flow model, through the props property. The same concept can be represented in vuex hasbelow,
Vue loader is a loader for webpack that allows you to author Vue components in a format called Single-FileComponents (SFCs).
For example, it authors HelloWorld component in a SFC,
<template><divclass="greeting">{{ message}}</div></template><script>exportdefault{data(){return{message:'Hello world for vueloader!'}}}</script><style>.greeting{color:blue;}</style>
Vue Loader's configuration is a bit different from other loaders by adding Vue Loader's plugin to your webpackconfig. The vue loader plugin is required for cloning any other rules(js and css rules) defined and applying themto the corresponding language blocks(<script> and <style>) in .vue files.
For example, the simple demonistration of webpack configuration for vue loader would be as below,
// webpack.config.jsconstVueLoaderPlugin=require('vue-loader/lib/plugin')module.exports={mode:'development',module:{rules:[{test:/\.vue$/,loader:'vue-loader'},// this will apply to both plain `.js` files and `<script>` blocks in `.vue` files{test:/\.js$/,loader:'babel-loader'},// this will apply to both plain `.css` files and `<style>` blocks in `.vue` files{test:/\.css$/,use:['vue-style-loader','css-loader']}]},plugins:[// make sure to include the plugin for cloning and mapping them to respective language blocksnewVueLoaderPlugin()]}
Below are the list of Asset URL transform rules
- Absolute path: If the URL is an absolute path (for example, /images/loader.png)then it will be preservedas-is.
- Relative path: If the URL starts with
.
(for example, ./images/loader.png) then it will be interpreted asa relative module request and resolved based on the folder structure on your file system. - URLs starts with ~ symbol: If the URL starts with
~
symbol(for example, ./some-node-package/loader.png)then it is interpreted as a module request. This way it can reference assets inside node modules too. - URLs starts with @ symbol: If the URL starts with
@
symbol then it is interpreted as a module request.This is useful if your webpack config has an alias for @, which by default points to/src
path.
Vue-loader
will automatically infer the proper loaders to use based on thelang
attribute of a language blockand the rules defined in webpack config. You can use pre-processors such asSASS,LESS, Stylus and PostCSS
usingvuejs loader.Scoped CSS is a mechanism in VueJS Single File Components(SFC) that prevents styles from leaking out of the currentcomponent and affecting other unintended components on your page. i.e, When a <style> tag has the scoped attribute,its CSS will apply to elements of the current component only. It uses PostCSS to transform scoped css to plain CSS.
Let's take an example usage of scoped css,
<stylescoped>.greeting{color:green;}</style><template><divclass="greeting">Let's start Scoped CSS</div></template>
The above code will be converted to plain CSS,
<stylescoped> .greeting[data-v-f3f3eg9]{color:green;}</style><template><divclass="greeting"data-v-f3f3eg9>Let's start Scoped CSS</div></template>
Yes, you can include both scoped and non-scoped styles in the same component. If you don't mention scoped attributethen it will become global style.
<style>/* global styles */</style><stylescoped>/* local styles */</style>
In scoped css, if you need to modify the styles of a child component using deep selectors(i,e from parent scopedcss) then you need to use>>> combinator.
For example, the scoped deep selector on parent scoped css would be as below,
<stylescoped>.class1>>>.class2{/* ... */}</style>
It will be converted as,
.class1[data-v-f3f3eg9].class2{/* ... */}
Note: If you preprocessors such as SASS then it may not be able to processs >>> properly. In such cases use the/deep/ or ::v-deep combinator instead >>> combinator.
The parent component's styles will not leak into child components. But a child component's root node will beaffected by both the parent's scoped CSS and the child's scoped CSS. i.e, your child component's root element has aclass that also exists in the parent component, the parent component's styles will leak to the child. Anyway thisis by design so that the parent can style the child root element for layout purposes.
For example, the background color property of parent component leaked into child component as below,
//parent.vue
<template><divclass="wrapper"><p>parent</p><ChildMessageComponent/></div></template><script>importChildMessageComponentfrom"./components/child";exportdefault{name:"App",components:{ ChildMessageComponent}};</script><stylescoped>.wrapper{background:blue;}</style>
//child.vue
<template><divclass="wrapper"><p>child</p></div></template><script>exportdefault{name:"Hello, Scoped CSS",};</script><stylescoped>.wrapper{background:red;}</style>
Now the background color of child wrapper is going to be blue instead red.
The scoped css style doesn't impact v-html directive's dynamically generated content. In this case, you can usedeep selectors to solve this styling issue.
Yes, vue-loader provides first-class integration with CSS Modules as an alternative for simulated scoped CSS.
No, templates (or any Vue-specific HTML) are ONLY allowed in .vue files and render functions are required in othercases.
Below are the steps to use css modules in VueJS,
- Enable CSS modules: CSS Modules must be enabled by passing modules: true option to css-loader
// webpack.config.js{module:{rules:[// ... other rules omitted{test:/\.css$/,use:['vue-style-loader',{loader:'css-loader',options:{// enable CSS Modulesmodules:true,// customize generated class nameslocalIdentName:'[local]_[hash:base64:8]'}}]}]}}
- Add module attribute: Add the module attribute to your
<style>
<stylemodule>.customStyle{background:blue;}</style>
- Inject CSS modules: You can inject CSS modules object with computed property $style
<template><div :class="$style.blue">Backgroundcolorshouldbeinblue</p></template>
It can work with object/array syntax of :class binding.
Yes, you can use preprocessors with CSS Modules.
For example, sass-loader can configured in webpack file for sass preprocessor.
// webpack.config.js -> module.rules{test:/\.scss$/,use:['vue-style-loader',{loader:'css-loader',options:{modules:true}},'sass-loader']}
You can customize the name of the injected computed property by giving the module attribute a value. This will behelpful to avoid overwriting injected styled if you have more than one <style> tags in a single *.vue component.
For example, you can use module attribute as below,
<stylemodule="a"> /* identifiers injected as a */</style><stylemodule="b">/* identifiers injected as b */</style>
Hot reloading is not about reloading the page when you edit any .vue file. Instead, when you edit a *.vue file, allinstances of that component will be swapped in without reloading the page. It improves the development experiencewhen you are tweaking the templates or styling of your components.
Hot Reload is always enabled except below situations:
- webpack target is node (SSR)
- webpack minifies the code
- process.env.NODE_ENV === 'production'
You can use
hotReload: false
option to disable the Hot Reload explicitly.It can be configured as below,
module:{rules:[{test:/\.vue$/,loader:'vue-loader',options:{hotReload:false// disables Hot Reload}}]}
The vue loader plugin internally uses hot reloading. If you are scaffolding project with
vue-cli
, hot reloadingcomes out of the box but if you are manually setting up the project then hot reloading is enabled by default withwebpack-dev-server --hot
command.Below are the state preservation rules in hot reloading,
- When editing the
<template>
of a component, instances of the edited component will re-render in place,preserving all current private state. - When editing the
<script>
part of a component, instances of the edited component will be destroyed andre-created in place. - When editing the
<style>
hot reload operates on its own via vue-style-loader without affecting applicationstate.
- When editing the
You can create functional components by adding functional attribute to template block,
<templatefunctional><div>{{props.msg}}</div></template>
If you need to access properties defined globally on
Vue.prototype
then you can access them on parent,<templatefunctional><div>{{parent.$someProperty}}</div></template>
You can perform testing in two ways,
- Using vue-cli: It offers pre-configured unit testing and e2e testing setups
- Manual setup: You can manually setting up unit tests for *.vue files using either mocha-webpack or jest
The stylelint linter supports linting style parts of Vue single file components. You can run linter on particularvue file as below,
stylelintMyComponent.vue
Other option is configuring stylelint-webpack-plugin in webpack. It can be configured as a dev dependency.
// webpack.config.jsconstStyleLintPlugin=require('stylelint-webpack-plugin');module.exports={// ... other optionsplugins:[newStyleLintPlugin({files:['**/*.{vue,htm,html,css,sss,less,scss,sass}'],})]}
The official
eslint-plugin-vue
supports linting both the template and script parts of Vue single file components.You can configure plugin in your ESLint config,// .eslintrc.jsmodule.exports={extends:["plugin:vue/essential"]}
You can run linter on particular component as below,
eslint--extjs,vueMyComponent.vue
You can use
eslint-loader
for *.vue files in order to automatically linted on save during development. It can beinstalled as npm module,npminstall-Deslinteslint-loader
After that you need to add it as pre-loader,
// webpack.config.jsmodule.exports={// ... other optionsmodule:{rules:[{enforce:'pre',test:/\.(js|vue)$/,loader:'eslint-loader',exclude:/node_modules/}]}}
CSS Extraction
is used to extract all the processed CSS in all Vue components into a single CSS file. Forwebpack4, you need to install below npm command,npminstall-Dmini-css-extract-plugin
You can configure this plugin in webpack as below,
// webpack.config.jsvarMiniCssExtractPlugin=require('mini-css-extract-plugin')module.exports={// other options...module:{rules:[// ... other rules omitted{test:/\.css$/,use:[process.env.NODE_ENV!=='production' ?'vue-style-loader' :MiniCssExtractPlugin.loader,'css-loader']}]},plugins:[// ... Vue Loader plugin omittednewMiniCssExtractPlugin({filename:'style.css'})]}
You can define custom language blocks inside *.vue files based on the
lang
attribute of the block, the block'stag name, and the rules in your webpack config. You can also useresourceQuery
to match a rule against a customblock with no lang.For example, to match against
<message>
custom blocks.{module:{rules:[{resourceQuery:/blockType=message/,loader:'loader-to-use'}]}}
Below are the list of major stylelint features
- It has more than160 built-in rules to catch errors, apply limits and enforce stylistic conventions
- Understandslatest CSS syntax including custom properties and level 4 selectors
- Itextracts embedded styles from HTML, markdown and CSS-in-JS object & template literals
- ParsesCSS-like syntaxes like SCSS, Sass, Less and SugarSS
- SupportsPlugins for reusing community plugins and creating own plugins
Vuex enforces below rules to structure any application.
- Application-level state is centralized in the store.
- The only way to mutate the state is by committing mutations, which are synchronous transactions.
- Asynchronous logic should be encapsulated in, and can be composed with actions.
The project structure for any non-trivial application would be as below,
Yes, vuex supports hot-reloading for mutations, modules, actions and getters during development. You need to useeither webpack's hot module replacement API or browserify's hot module replacement plugin.
The store.hotUpdate() API method is used for mutations and modules.
For example, you need to configure vuex store as below,
// store.jsimportVuefrom'vue'importVuexfrom'vuex'importmutationsfrom'./mutations'importmyModulefrom'./modules/myModule'Vue.use(Vuex)conststate={message:"Welcome to hot reloading"}conststore=newVuex.Store({ state, mutations,modules:{moduleA:myModule}})if(module.hot){// accept actions and mutations as hot modulesmodule.hot.accept(['./mutations','./modules/newMyModule'],()=>{// Get the updated modulesconstnewMutations=require('./mutations').defaultconstnewMyModule=require('./modules/myModule').default//swap in the new modules and mutationsstore.hotUpdate({mutations:newMutations,modules:{moduleA:newMyModule}})})}
Since mutations are just functions that completely rely on their arguments it will be easier to test. You need tokeep mutations inside your store.js file and should also export the mutations as a named export apart from defaultexport.
Let's take an example of increment mutations,
// mutations.jsexportconstmutations={increment:state=>state.counter++}
And test them using mocha and chai as below,
// mutations.spec.jsimport{expect}from'chai'import{mutations}from'./store'// destructure assign `mutations`const{ increment}=mutationsdescribe('mutations',()=>{it('INCREMENT',()=>{// mock stateconststate={counter:10}// apply mutationincrement(state)// assert resultexpect(state.counter).to.equal(11)})})
It is easier to test getters similar to mutations. It is recommended to test these getters if they have complicatedcomputation.
Let's take a simple todo filter as a getter
// getters.jsexportconstgetters={filterTodos(state,status){returnstate.todos.filter(todo=>{returntodo.status===status})}}
And the test case for above getter as follows,
// getters.spec.jsimport{expect}from'chai'import{getters}from'./getters'describe('getters',()=>{it('filteredTodos',()=>{// mock stateconststate={todos:[{id:1,title:'design',status:'Completed'},{id:2,title:'testing',status:'InProgress'},{id:3,title:'development',status:'Completed'}]}// mock getterconstfilterStatus='Completed'// get the result from the getterconstresult=getters.filterTodos(state,filterStatus)// assert the resultexpect(result).to.deep.equal([{id:1,title:'design',status:'Completed'},{id:2,title:'development',status:'Completed'}])})})
By proper mocking, you can bundle tests with webpack and run them on node without having depenceny on Browser API.It involves 2 steps,
Create webpack config: Create webpack config with proper .babelrc
// webpack.config.jsmodule.exports={entry:'./test.js',output:{path:__dirname,filename:'test-bundle.js'},module:{loaders:[{test:/\.js$/,loader:'babel-loader',exclude:/node_modules/}]}}
Run testcases: First you need to bundle and then run them using mocha as below,
webpackmochatest-bundle.js
Below are the steps to run tests in real browser,
- Install
mocha-loader
. - Configure webpack config entry point to 'mocha-loader!babel-loader!./test.js'.
- Start webpack-dev-server using the config.
- Go to
localhost:8080/webpack-dev-server/test-bundle
to see the test result
- Install
In strict mode, whenever Vuex state is mutated outside of mutation handlers, an error will be thrown. It make surethat all state mutations can be explicitly tracked by debugging tools. You can just enable this by passing
strict: true
while creating the vuex store.conststore=newVuex.Store({// ...strict:true})
No, it is not recommended to use strict mode in production environment. Strict mode runs a synchronous deep watcheron the state tree for detecting inappropriate mutations and it can be quite expensive when you perform large amountof mutations. i.e, It can impact performance if you enable in production mode.Hence, it should be handled through build tools,
conststore=newVuex.Store({// ...strict:process.env.NODE_ENV!=='production'})
The vuex plugin is an option hat exposes hooks for each mutation. It is a normal function that receives the storeas the only argument. You can create your own plugin or use built-in plugins.The plugin skeleton would be as below,
constmyPlugin=store=>{// called when the store is initializedstore.subscribe((mutation,state)=>{// called after every mutation.// The mutation comes in the format of `{ type, payload }`.})}
After that plugin can be configured for plugins options as below,
conststore=newVuex.Store({// ...plugins:[myPlugin]})
Similar to components you can't mutate state directly but they can trigger changes by by committing mutations. Thisway a plugin can be used to sync a data source to the store.
For example, createWebSocketPlugin plugin is used to sync a websocket data source to the store.
exportdefaultfunctioncreateWebSocketPlugin(socket){returnstore=>{socket.on('data',data=>{store.commit('receiveData',data)})store.subscribe(mutation=>{if(mutation.type==='UPDATE_DATA'){socket.emit('update',mutation.payload)}})}}
And then configure plugin in vuex store as below
constplugin=createWebSocketPlugin(socket)conststore=newVuex.Store({ state, mutations,plugins:[plugin]})
A Vuex "store" is basically a container that holds your application state. The store creation is prettystraightforward.
Below are the list of instructions to use vuex in an increment application,
- Configure vuex in vuejs ecosystem
importVuexfrom"vuex";Vue.use(Vuex)
- Provide an initial state object and some mutations
// Make sure to call Vue.use(Vuex) first if using a module systemconststore=newVuex.Store({state:{count:0},mutations:{increment(state){state.count++}}})
- Trigger state change with commit and access state variables,
store.commit('increment')console.log(store.state.count)// -> 1
- Configure vuex in vuejs ecosystem
Below are the two major differences between vuex store and plain global object,
- Vuex stores are reactive: If the store's state changes then vue components will reactively and efficientlyget updated
- Cannot directly mutate the store's state: The store's state is changed by explicitly committing mutations toensure that every state change leaves a track-able record for tooling purpose
We want to explicitly track application state in order to implement tools that can log every mutation, take statesnapshots, or even perform time travel debugging. So we need to commit a mutation instead of changing store's statedirectly.
Vuex's single state tree is single object contains all your application level state and serves as the "singlesource of truth". It does not conflict with modularity when you split state and mutations into sub modules.
You can install vuex using npm or yarn as below,
npminstallvuex--save(or)yarnaddvuex
In a module system, you must explicitly install Vuex via Vue.use()
importVuefrom'vue'importVuexfrom'vuex'Vue.use(Vuex)
(OR)
You can also install it using CDN links such as unpkg.cpm which provides NPM-based CDN links. Just include vuexafter Vue and it will install itself automatically.
<scriptsrc="https://unpkg.com/vue.js"></script><scriptsrc="https://unpkg.com/vuex.js"></script>
Note: You can use a specific version/tag via URLs likehttps://unpkg.com/vuex@2.0.0. If you don't mention anyversion then it will point to latest version.
Yes, vuex requires Promise. If your supporting browsers do not implement Promise (e.g. IE), you can use a polyfilllibrary, such as es6-promise using npm or yarn.
npminstalles6-promise--save #NPMyarnaddes6-promise #Yarn
After that import into anywhere in your application,
import'es6-promise/auto'
Since Vuex stores are reactive, you can retrieve" state from store by simply returning store's state from within acomputed property. i.e, Whenever store state changes, it will cause the computed property to re-evaluate, andtrigger associated DOM updates.
Let's take a hello word component which display store's state in the template,
// let's create a hello world componentconstGreeting={template:`<div>{{ greet }}</div>`,computed:{greet(){returnstore.state.msg}}}
Vuex provides a mechanism to "inject" the store into all child components from the root component with the storeoption. It will be enabled by vue.use(vuex).
For example, let's inject into our app component as below,
constapp=newVue({el:'#app',// provide the store using the "store" option.// this will inject the store instance to all child components. store,components:{ Greeting},template:` <div> <greeting></greeting> </div> `})
Now the store will be injected into all child components of the root and will be available on them as **this.$store**
// let's create a hello world componentconstGreeting={template:`<div>{{ greet }}</div>`,computed:{greet(){returnthis.$store.state.msg}}}
In Vuex application, creating a computed property every time whenever we want to access the store's state propertyor getter is going to be repetitive and verbose, especially if a component needs more than one state property. Inthis case, we can make use of the mapState helper of vuex which generates computed getter functions for us.
Let's take an increment example to demonstrate mapState helper,
// in full builds helpers are exposed as Vuex.mapStateimport{mapState}from'vuex'exportdefault{// ...computed:mapState({// arrow functions can make the code very succinct!username:state=>state.username,// passing the string value 'username' is same as `state => state.username`usernameAlias:'username',// to access local state with `this`, a normal function must be usedgreeting(state){returnthis.localTitle+state.username}})}
We can also pass a string array to mapState when the name of a mapped computed property is the same as a state subtree name
computed:mapState([// map this.username to store.state.username'username'])
You can use object spread operator syntax in order to combine mapState helper(which returns an object) with otherlocal computed properties. This way it simplify merging techniques using utilities.
computed:{localComputed(){/* ... */},// mix this into the outer object with the object spread operator ...mapState({// ...})}
No, if a piece of state strictly belongs to a single component, it could be just fine leaving it as local state.i.e, Even though vuex used in the application, it doesn't mean that you need to keep all the local state in vuexstore. Other than that the code becomes more verbose and indirect although it makes your state mutations moreexplicit and debuggable.
Vuex getters acts as computed properties for stores to compute derived state based on store state. Similar tocomputed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some ofits dependencies have changed.
Let's take a todo example which as completedTodos getter to find all completed todos,
conststore=newVuex.Store({state:{todos:[{id:1,text:'Vue course',completed:true},{id:2,text:'Vuex course',completed:false},{id:2,text:'Vue Router course',completed:true}]},getters:{completedTodos:state=>{returnstate.todos.filter(todo=>todo.completed)}}})
Note: Getters receive state as first argument.
You can access values of store's getter object(store.getters) as properties. This is known as property styleaccess.
For example, you can access todo's status as a property,
store.getters.todosStatus
The getters can be passed as 2nd argument for other getters. For example, you can derive completed todo's countbased on their status as below,
getters:{completedTodosCount:(state,getters)=>{returngetters.todosStatus==='completed'}}
Note: The getters accessed as properties are cached as part of Vue's reactivity system.
You can access store's state in a method style by passing arguments.
For example, you can pass user id to find user profile information as below,
getters:{getUserProfileById:(state)=>(id)=>{returnstate.users.find(user=>user.id===id)}}
After that you can access it as a method call,
store.getters.getUserProfileById(111);{id:'111',name:'John',age:33}
The mapGetters is a helper that simply maps store getters to local computed properties.
For example, the usage of getters for todo app would be as below,
import{mapGetters}from'vuex'exportdefault{computed:{// mix the getters into computed with object spread operator ...mapGetters(['completedTodos','todosCount',// ...])}}
Vuex mutations are similar to any events with a string
type
and ahandler
. The handler function is where weperform actual state modifications, and it will receive the state as the first argument.For example, the counter example with increment mutation would be as below,
conststore=newVuex.Store({state:{count:0},mutations:{increment(state){// mutate statestate.count++}}})
You can't directly invoke mutation instead you need to call
store.commit
with its type. The above mutation wouldbe triggered as followsstore.commit('increment')
You can also passpayload for the mutation as an additional argument to
store.commit
.For example, the counter mutation with payload object would be as below,
mutations:{increment(state,payload){state.count+=payload.increment}}
And then you can trigger increment commit
store.commit('increment',{increment:20})
Note: You can also pass primitives as payload.
You can also commit a mutation is by directly using an object that has atype property.
store.commit({type:'increment',value:20})
Now the entire object will be passed as the payload to mutation handlers(i.e, without any changes to handlersignature).
mutations:{increment(state,payload){state.count+=payload.value}}
Since a Vuex store's state is made reactive by Vue, the same reactivity caveats of vue will apply to vuexmutations. These are the rules should be followed for vuex mutations,
- It is recommended to initialize store's initial state with all desired fields upfront
- Add new properties to state Object either by set method or object spread syntax
Vue.set(stateObject,'newProperty','John')
(OR)
state.stateObject={ ...state.stateObject,newProperty:'John'}
You need to remember that mutation handler functions must be synchronous. This is why because any state mutationperformed in the callback is essentially un-trackable. It is going to be problematic when the devtool will need tocapture a "before" and "after" snapshots of the state during the mutations.
mutations:{someMutation(state){api.callAsyncMethod(()=>{state.count++})}}
You can commit mutations in components with eitherthis.$store.commit('mutation name') or mapMutations helperto map component methods tostore.commit calls.
For example, the usage of mapMutations helper on counter example would be as below,
import{mapMutations}from'vuex'exportdefault{methods:{ ...mapMutations(['increment',// map `this.increment()` to `this.$store.commit('increment')`// `mapMutations` also supports payloads:'incrementBy'// map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`]), ...mapMutations({add:'increment'// map `this.add()` to `this.$store.commit('increment')`})}}
No, it is not mandatory. But you might observed that State management implementations such Flux and Redux useconstants for mutation types. This convention is just a preference and useful to take advantage of tooling likelinters, and putting all constants in a single file allows your collaborators to get an at-a-glance view of whatmutations are possible in the entire application.
For example, the mutations can be declared as below,
// mutation-types.jsexportconstSOME_MUTATION='SOME_MUTATION'
And you can configure them in store as follows,
// store.jsimportVuexfrom'vuex'import{SOME_MUTATION}from'./mutation-types'conststore=newVuex.Store({state:{ ...},mutations:{// ES2015 computed property name feature to use a constant as the function name[SOME_MUTATION](state){// mutate state}}})
In Vuex, mutations are synchronous transactions. But if you want to handle asynchronous operations then you shoulduseactions.
Actions are similar to mutations, but there are two main differences,
- Mutations perform mutations on the state, actions commit mutations.
- Actions can contain arbitrary asynchronous operations unlike mutations.
Vuex provides actions property similar mutations property in order to define action handlers. These action handlersreceive context object as an argument which has same properties and methods of store instance.
Let's see counter example to demonstrate increment action which commits respective mutation,
conststore=newVuex.Store({state:{count:0},mutations:{increment(state){state.count++}},actions:{increment(context){context.commit('increment')}}})
Actions are simply triggered with the store.dispatch method as below,
store.dispatch('increment')
Yes, actions support both payload and object style format similar to mutations.
// dispatch with a payloadstore.dispatch('incrementAsync',{amount:10})// dispatch with an objectstore.dispatch({type:'incrementAsync',amount:10})
Styled components is a CSS-in-JS library used mainly for ReactJS applications. If you want to use it for VueJSapplications, there is a VueJS styled components library port available, however, it is not a common practice.
You can dispatch actions in components withthis.$store.dispatch('action name'), or use themapActionshelper which maps component methods to store.dispatch calls.
For example, you can dispatch increment actions in counter component as below,
import{mapActions}from'vuex'exportdefault{// ...methods:{ ...mapActions(['increment',// map `this.increment()` to `this.$store.dispatch('increment')`// `mapActions` also supports payloads:'incrementBy'// map `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`]), ...mapActions({add:'increment'// map `this.add()` to `this.$store.dispatch('increment')`})}}
You can write multiple actions together to handle more complex async flows either by chaining promises orasync/await. i.e,
store.dispatch
can handle Promise returned by the triggered action handler and it also returnsPromise.Let's take two actions to see how they are combined and handled async flows,
actions:{actionOne({ commit}){returnnewPromise((resolve,reject)=>{setTimeout(()=>{commit('first mutation')resolve()},1000)})},actionTwo({ dispatch, commit}){returndispatch('actionA').then(()=>{commit('second mutation')})}}
As per the above example, When you try to dispatch actionTwo it dispatches actionOne first and then commitsrespective mutation. You can still simplify with async/await as below,
actions:{asyncactionOne({ commit}){commit('first mutation',awaitgetDataAsPromise())},asyncactionTwo({ dispatch, commit}){awaitdispatch('actionOne')// wait for `actionA` to finishcommit('second mutation',awaitgetSomeDataAsPromise())}}
If you keep all state of our application in a single big state, the store can get really bloated. To solve thisproblem, Vuex allows us to divide our store into modules. Here, each module can contain its own state, mutations,actions, getters, and even nested modules.
Let's take an example with multiple modules, configuring them in vuex and accessing different modules,
constmoduleOne={state:{ ...},mutations:{ ...},actions:{ ...},getters:{ ...}}constmoduleTwo={state:{ ...},mutations:{ ...},actions:{ ...},getters:{ ...}}conststore=newVuex.Store({modules:{one:moduleOne,two:moduleTwo}})store.state.one// -> `moduleOne's statestore.state.two// -> `moduleTwo's state
When you use modules the local state will be available to mutations, getters and actions in different ways.
- Both mutations and getters will receive module local state as first argument.
constmoduleOne={state:{count:0},mutations:{increment(state){state.count++;// Here state refers local module state}},getters:{average(state){returnstate.count/2}}}
- In actions, local state will be available as first argument.
constmoduleOne={actions:{incrementConditional({ state, commit, rootState}){if(state.count<rootState.count){commit('increment')}}}}
- Both mutations and getters will receive module local state as first argument.
By default, actions, mutations and getters inside modules are still registered under the global namespace. Becauseof that multiple modules react to the same mutation/action type.
Sometime you may need to create multiple instances of a module.
For example, it is needed in the below cases,
- If multiple stores that use the same module
- Register the same module multiple times in the same store.
In those cases, you need to assign to a variable and export it for reusability,
constMyReusableModule={// state// mutations, actions, getters...}
Vuex enforces below high-level principles,
- The Application-level state need to be centralized in the store
- The state should be mutated by committing mutations only(i.e, for synchronous transactions)
- The actions should be used for asynchronous transactions.
In strict mode, you can't mutate state directly using
v-model
attribute. If you use v-model it throws an errorbecause mutation is not performed inside an explicit Vuex mutation handler.For example, the below input throws an error due to v-model usage
<inputv-model="stateObject.message">
In this case, you need to bind the 's value. It can be resolved using value attribute as below,
<input :value="username" @input="updateProfile">computed:{ ...mapState({username:state=>state.user.username})},methods:{updateProfile(e){this.$store.commit('updateProfile',e.target.value)}},mutations:{updateProfile(state,username){state.user.username=username}}
You can still use model directive using two-way computed property with a setter.
<inputv-model="username"> computed:{username:{get(){returnthis.$store.state.user.username},set(value){this.$store.commit('updateProfile',value)}}} mutations:{updateProfile(state,username){state.user.username=username}}
Vue CLI is a simple command line interface for scaffolding Vue.js projects. It will be helpful for rapid Vue.jsdevelopment. You can install the npm package globally as below,
npminstall-g @vue/cli#ORyarnglobaladd @vue/cli
You can find the install version using
vue --version
command.Note: Vue CLI requires Node.js version 8.9 or above (8.11.0+ recommended).VueCLI provides below major features,
- Interactive project scaffolding via @vue/cli
- A rich collection of official plugins integrating the best tools in the frontend ecosystem.
- A full graphical user interface to create and manage Vue.js projects.
- Zero config rapid prototyping via combination of @vue/cli and @vue/cli-service-global
- A runtime dependency (@vue/cli-service) built on top of webpack and extensible via plugins
In Vue CLI, Instant prototyping is known as rapid prototyping with just a single *.vue file with the
vue serve
(similar to vue create) andvue build
commands. But you need to install below global addon for this.npminstall-g @vue/cli-service-global#oryarnglobaladd @vue/cli-service-global
You can also provide entry component for
vue serve
and target file forvue build
using below commandsvueserveMyComponent.vuevuebuildMyComponent.vue
You can create project using
vue create
commandvuecreatemy-app
You can either choose the default preset or select "Manually select features" to pick the features you need.
The default preset prompt would be as below,
and the manual select features would be as below,
You can also create and manage projects using a graphical interface with the
vue ui
command. Once you apply thiscommand, it opens a browser window with a GUI that guides you through the project creation process.Vue CLI uses a plugin-based architecture where each plugin can modify the internal webpack configuration and injectcommands to
vue-cli-service
. i.e, Each feature is implemented as a plugin. This architecture makes Vue CLIflexible and extensible.You can install a plugin into an already created project with the
vue add
command.vueadd @vue/eslint(OR)vueadd @vue/cli-plugin-eslint
You can also add options for plugin
vueadd @vue/eslint--configairbnb--lintOnsave
If a plugin is already installed, you can skip the installation and only invoke its generator with the
vue invoke
command.If you need access to the plugin API in your project without creating a full plugin, you can use the
vuePlugins.service
option in your package.json file{"vuePlugins":{"service":["my-service.js"]}}
You can also add files that will behave like UI plugins with the
vuePlugins.ui
option{"vuePlugins":{"ui":["my-ui.js"]}}
A Vue CLI preset is a JSON object that contains pre-defined options and plugins for creating a new project withoutinteractive prompts to select them. During project creation(using vue create), the presets will be saved in a
~/.vuerc
which can modified at any time.For example, the generated JSON object(or preset) would be as below
{"useConfigFiles":true,"router":true,"vuex":true,"cssPreprocessor":"sass","plugins":{"@vue/cli-plugin-babel":{},"@vue/cli-plugin-eslint":{"config":"airbnb","lintOn":["save","commit"]}}}
You can explicitly specify versions of the plugins being used.
{"plugins":{"@vue/cli-plugin-eslint":{"version":"^3.0.0",// ... other options for this plugin}}}
For official plugins, the CLI will automatically use the latest version available in the registry
Each plugin can inject its own prompts during the project creation process irrespective of preset declarationsusingprompts: true setting
For example, user can pick their own ESLint config using the below configuration
{"plugins":{"@vue/cli-plugin-eslint":{// let the users pick their own ESLint config"prompts":true}}}
You can share a preset with other developers by publishing it in a git repo. The repo can be published in eithergithub, GitLab or BitBucket.The repo will contain below files,
- preset.json: The main file containing the preset data and it is required.
- generator.js: A generator that can inject or modify files in the project.
- prompts.js: A prompts file that can collect options for the generator.You can apply
--preset
option to use remote presets while creating the project#usepresetfromGitHubrepovuecreate--presetusername/repomy-project
Yes, Vue CLI will load local presets if the value for the --preset option is a relative or absolute file path, orends with .json. i.e, You can work with local presets directly. These local presets avoids repeatedly pushing thepreset to a remote repo to test.
// Directory contains preset.json filevuecreate--preset./my-presetmy-project(OR)vuecreate--presetmy-preset.jsonmy-project
The
browserslist
option is available in package.json file in order to specify a range of browsers the project issupported. This value is going to be used by babel and autoprefixer to transpile javascript features and applyingvendor prefixes.For example, you can declare it as follows,
"browserslist":["last 1 version","> 1%","IE 10"]
The community plugins and components might need different strategies for different versions. In this case, you canuseVue.version which provides installed version of Vue as a string.
For example, you can implement different logic based on different versions
letversion=Number(Vue.version.split('.')[0])if(version===2){// Vue v2.x.x}elseif(version===1){// Vue v1.x.x}else{// Unsupported versions of Vue}
In version 3.0 you can create a reactive object with the reactive() API.
constreactiveState=reactive({count:0})
In version 2.6, you can create reactive objects with Vue.observable() global API.
constreactiveState=Vue.observable({count:0})
These observable objects can be used directly in computed properties and render functions.
constDemo={render(h){returnh('button',{on:{click:()=>{reactiveState.count++}}},`count is:${state.count}`)}}
In Vue 2.6 version, the new slot syntax is provided using v-slot directive which aligns syntax with Vue 3.0. Thisis going to be replacement for old slot syntax.
The comparison for old and new slot syntax:
<!-- old --><user><templateslot="header"slot-scope="{ msg }"> text slot:{{ msg}}</template></user><!--new --><user><templatev-slot:header="{ msg }"> text slot:{{ msg}}</template></user>
VueJS provides compile method which is used to compile a template string into a render function. This method isonly available in the full build.
For example, you can compile template message:
varresult=Vue.compile('<div><span>{{ msg }}</span></div>')newVue({data:{msg:'Welcome to Vue world'},render:result.render,staticRenderFns:result.staticRenderFns})
The nextTick method is just a comfortable way to execute a function after the data has been set, and the DOM hasbeen updated. As an example, the usage is going to be similar to setTimeout:
// modify datavm.msg='Welcome to Vue'// DOM not updated yetVue.nextTick(function(){// DOM updated})// usage as a promise (2.1.0+)Vue.nextTick().then(function(){// DOM updated})
From 2.6 version onwards, Vue's built-in error handling mechanism can capture errors inside v-on handlers. Also,ifany of your lifecycle hooks or event handlers performs asynchronous operations, you can now return a Promise fromthe function so that any uncaught errors from that Promise chain are also sent to your error handlers.
Let's take an example of mounted lifecycle hook,
exportdefault{asyncmounted(){// if an async error is thrown here, it now will get// caught by errorCaptured and Vue.config.errorHandlerthis.todos=awaitapi.getTodos()}}
In Vue 2.6 release onwards, Directive arguments can now accept dynamic JavaScript expressions. i.e, the specificargument that we want to use is only known at runtime.
Let's assign dynamic key and event directives for a div element,
<divv-bind:[key]="value"></div><divv-on:[event]="handler"></div>
Apart from the benefits of dynamic directives arguments, it brings two drawbacks or considerations on the usage
- Constraints on expressions: When you perform complex JavaScript expressions, make sure that html attributenames cannot contain spaces and quotes.The below expression doesn't work as expected
<div :[key+'unique']="value"></div>
Instead you may need to use string template syntax
<div :[`${key}unique`]="value"></div>
- Custom Directives: The custom directive implementations need to have potential argument changes in additionto value changes.
Dynamic argument values are expected to be strings but it allows
null
as a special value that explicitlyindicates that the binding should be removed. Other types will be treated as mistakes and will trigger a warning.So null value can be applied for v-bind and v-on.No. It can be applied only for v-bind and v-on but not v-slot. This is because v-slot is not a binding and cannotbe removed.
Vue I18n is an internationalization plugin of Vue.js. It easily integrates some localization features to yourVue.js Application.
The simple usage with in html would be as below,
<scriptsrc="https://unpkg.com/vue/dist/vue.js"></script><scriptsrc="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script><divid="app"><p>{{$t("user.message")}}</p></div>
and after that configure them in javascript
// Ready translated locale messagesconstmessages={en:{user:{message:'Good morning'}},de:{user:{message:'Guten Morgen'}}}// Create VueI18n instance with optionsconsti18n=newVueI18n({locale:'de',// set locale messages,// set locale messages})// Create a Vue instance with `i18n` optionnewVue({ i18n}).$mount('#app')
The output is going to be like this,
Guten Morgen
Basically there are 4 types of formatting available in i18n plugin,
- Named formatting: First You need to define the message keys in curly braces({})
constmessages={en:{message:{greeting:'{msg} Morning'}}}
After that pass argument value along with key in the template
<p>{{$t('message.greeting',{msg:'Good'})}}</p>
It outputs the result as below,
<p>Good Morning</p>
- List formatting: First you need to define zero index based keys in the messages,
constmessages={en:{message:{greeting:'{0} Morning'}}}
After that pass argument value with in an array
<p>{{$t('message.greeting',['Good'])}}</p>
Finally it outputs the result as below,
<p>Good morning</p>
Note: It also accepts array-like object
<p>{{$t('message.greeting',{'0':'Good'})}}</p>
- HTML formatting: This formatting is required when want to render your translation as an HTML message and nota static string.
constmessages={en:{message:{greeting:'Good <br> Morning'}}}
After that use it in the html directive template as below
<pv-html="$t('message.greeting')"></p>
Finally it outputs the result as below
<p>Good<!--<br> exists but is rendered as html and not a string-->Morning</p>
- Ruby on rails format: First you need to define with percentile and curly braces as below,
constmessages={en:{message:{greeting:'%{msg} Morning'}}}
After that pass argument with key similar to named formatting
<p>{{$t('message.greeting',{msg:'Good'})}}</p>
Finally it renders the output as below,
<p>Good Morning</p>
- Named formatting: First You need to define the message keys in curly braces({})
You can use custom formatting for some of the formatting cases such as ICU formatting syntax (message "pattern"strings with variable-element placeholders enclosed in {curly braces}). It implement Formatter Interface.
// Custom Formatter implementationclassCustomFormatter{constructor(options){// ...}interpolate(message,values){// return the interpolated arrayreturn['resolved message string']}}// register with `formatter` optionconsti18n=newVueI18n({locale:'en-US',formatter:newCustomFormatter(/* here the constructor options */),messages:{'en-US':{// ...},// ...}})// Run!newVue({ i18n}).$mount('#app')
You can translate with pluralization by defining the locale that have a pipe | separator, and define plurals inpipe separator. Remember that template should use $tc() instead of $t().
First you need to define the messages,
constmessages={en:{user:'user | users',friend:'no friend | one friend | {count} friends'}}
And the template can configure the messages with values
<p>{{$tc('user',1)}}</p><p>{{$tc('user',10)}}</p><p>{{$tc('friend',0)}}</p><p>{{$tc('friend',1)}}</p><p>{{$tc('friend',10,{count:10})}}</p>
Finally it outputs the result as below
<p>user</p><p>users</p><p>no friend</p><p>onefriend</p><p>10 friends</p>
You can localize the datetime with definition formats(e.g. short, long, etc).
Lets follow below steps to localize date and time,
For example, you can add definition formats for English and Jappan locale as below
constdateTimeFormats={'en-US':{short:{year:'numeric',month:'short',day:'numeric'},long:{year:'numeric',month:'short',day:'numeric',weekday:'short',hour:'numeric',minute:'numeric'}},'ja-JP':{short:{year:'numeric',month:'short',day:'numeric'},long:{year:'numeric',month:'short',day:'numeric',weekday:'short',hour:'numeric',minute:'numeric',hour12:true}}}
After that You need to specify the dateTimeFormats option of VueI18n constructor
consti18n=newVueI18n({ dateTimeFormats})newVue({ i18n}).$mount('#app')
And then add them to the template
<divid="app"><p>{{$d(newDate(),'short')}}</p><p>{{$d(newDate(),'long','ja-JP')}}</p></div>
Finally it outputs the result
<divid="app"><p>May 20, 2019</p><p>2019年5月20日</p></div>
You can localize the number with definition formats(e.g. currency, etc)
Lets follow below steps to localize numbers,
- You need to add definition formats. For example, lets add it for English and Japanese locales
constnumberFormats={'en-US':{currency:{style:'currency',currency:'USD'}},'ja-JP':{currency:{style:'currency',currency:'JPY',currencyDisplay:'symbol'}}}
- After that specify the numberFormats option of VueI18n constructor
consti18n=newVueI18n({ numberFormats})newVue({ i18n}).$mount('#app')
- Now let's configure them in template
<divid="app"><p>{{$n(10,'currency')}}</p><p>{{$n(50,'currency','ja-JP')}}</p></div>
- Finally it outputs the result
<divid="app"><p>$10.00</p><p>¥50</p></div>
All child components of a root instance are localized using the locale property of the VueI18n class. You canchange the value of the locale property of the VueI18n instance as below.
consti18n=newVueI18n({locale:'de',// set locale ...})// create root Vue instancenewVue({ i18n, ...}).$mount('#app')// change other localei18n.locale='en'
You can also use component's VueI18n instance referenced as the $i18n property which will be used to change thelocale.
<template><divclass="locale-changer"><selectv-model="$i18n.locale"><optionv-for="(lang, i) in langs" :key="`Lang${i}`" :value="lang">{{ lang}}</option></select></div></template><script>exportdefault{name:'locale-changer',data(){return{langs:['de','en']}}}</script>
The loading of all translation files at once is unnecessary and it may impact the performance too. It will be easyfor lazy loading or asynchronously loading the translation files when you use webpack. i.e, You can dynamicallyload or import language translations using webpack as below,
//i18n-setup.jsimportVuefrom'vue'importVueI18nfrom'vue-i18n'importmessagesfrom'@/lang/en'importaxiosfrom'axios'Vue.use(VueI18n)exportconsti18n=newVueI18n({locale:'en',// set localefallbackLocale:'en', messages// set locale messages})constloadedLanguages=['en']// our default language that is preloadedfunctionsetI18nLanguage(lang){i18n.locale=langaxios.defaults.headers.common['Accept-Language']=langdocument.querySelector('html').setAttribute('lang',lang)returnlang}exportfunctionloadLanguageAsync(lang){if(i18n.locale!==lang){if(!loadedLanguages.includes(lang)){returnimport(/* webpackChunkName: "lang-[request]" */`@/lang/${lang}`).then(msgs=>{i18n.setLocaleMessage(lang,msgs.default)loadedLanguages.push(lang)returnsetI18nLanguage(lang)})}returnPromise.resolve(setI18nLanguage(lang))}returnPromise.resolve(lang)}
After that loadLanguageAsync function can be used inside a vue-router beforeEach hook.
router.beforeEach((to,from,next)=>{constlang=to.params.langloadLanguageAsync(lang).then(()=>next())})
The main difference between a computed property and a method is that computed properties are cached andinvoke/change only when their dependencies change. Whereas a method will evaluate every time it's called.
Vuetify is a semantic component material framework for Vue. It aims to provide clean, semantic and reusablecomponents that make building application easier. The installation and configuration is simple as below,
npminstallVuetify
importVuefrom'vue'importVuetifyfrom'vuetify'// Import Vuetify to your projectVue.use(Vuetify)// Add Vuetify as a plugin
You can use deep watcher by setting
deep: true
in the options object. This option enables us to detect nestedvalue changes inside Objects.vm.$watch('someObject',callback,{deep:true})vm.someObject.nestedValue=123// callback is fired
Note: This is not required to listen for Array mutations.
You can use
immediate: true
option in order to trigger watchers when the vue instance (or component) is beingcreated. i.e This option will trigger the callback immediately with the current value of the expression.watch:{test:{immediate:true,handler(newVal,oldVal){console.log(newVal,oldVal)},},},
When
comments
option enabled, it will preserve and render HTML comments found in templates. By default, it'svalue is false. Let's see the action in an example,<template><divclass="greeting"> <!--greeting--><h1>{{ msg}}</h1></div></template><script>exportdefault{comments:true,data(){return{msg:'Good morning'}}}</script>
Note: This option is only available in the full build, with in-browser compilation. i.e, It won't work withSingle File Components(SFC).
You can use
vm.$isServer
method to know whether the current Vue instance is running on the server or client.The usage would be as below,
constVue=require('vue');Vue.prototype.$isServer(OR)this.$isServer// With in component
You can setup a watcher on the
$route
in your component. It observes for route changes and when changed ,sets themessage property.watch:{$route(to,from){this.message='Welcome';}}
You can use
vue-router-sync
library to sync current $route object in vuex store's state.The usage is quite straight forward with two steps
- Installation:
npm install vuex-router-sync
- Sync router and store:
import{sync}from'vuex-router-sync'importstorefrom'./vuex/store'// vuex store instanceimportrouterfrom'./router'// vue-router instanceconstunsync=sync(store,router)// Returns an unsync callback functionunsync()// Unsyncs store from router
- Installation:
The navigation guards of vue-router are used to protect navigations either by redirecting it or canceling it.
Below are the 3 different ways to hook into router navigations
- Global:
- Per-route:
- In-component:
Yes, you can access it directly as you would data props.
For example, the comboTwo computed property uses comboOne computed property as below,
data(){return{propOne:'prop1',propTwo:'prop2'}},computed:{comboOne(){returnthis.propOne+','+this.propTwo;},comboTwo(){returnthis.comboOne.split(',').join('-');}}
The variables need to be exposed on your data in order to use them in template section. i.e, You can't use themdirectly on template.
<span> CREATE:{{CREATE_PROP}} UPDATE:{{UPDATE_PROP}} DELETE:{{DELETE_PROP}}</span><script>import{CREATE_DATA,UPDATE_DATA,DELETE_DATA}from'constants';newVue({ ...data:{CREATE_PROP:CREATE_DATA,UPDATE_PROP:UPDATE_DATA,DELETE_PROP:DELETE_DATA}...})</script>
No, it is not recommended. Computed properties should be synchronous. But if you still use asynchronous actionsinside them, they may not work as expected and can lead to an unexpected behaviour.
For example, the below usage of async/await is not recommended,
asyncsomeComputedProperty(){returnawaitsomeFunction()},
Note: If you still prefer to use async computed properties for some reason then you can consider usingadditional plugin such as
vue-async-computed
.The component data must be a function instead directly providing the object. This is because each instance needs tomaintain an independent copy of the returned data object. Otherwise one component instance data changes will impactthe data of all other instances.
For example, the below code snippets gives an idea on correct approach,
data:{// Badmessage:'Hello'} data:function(){//Goodreturn{message:'Hello'}}
Component names should always be multi-word, except for root level or built-in vue components(such as
<transition>
or<component>
etc). This recommendation is to prevent conflicts with existing and future HTMLelements, since all HTML elements are a single word.Vue.component('user',{//bad approach// ...})Vue.component('user-profile',{//good approach// ...})
The best way to force Vue to re-render a component is to set a
:key
on the component. i.e, Whenever the componentto be re-rendered, just change the value of the key then Vue will re-render the component.⬆ Back to TopVueJS automatically detects the changes to ref's value and updates the DOM with a dependency-tracking basedreactivity system.
- When a component is rendered for the first time, it tracks every ref that was used during the render.
- Whenever a ref is mutated, it will trigger a re-render of the component.
Vue 2.0 does not have native support for the Composition API, but you can use it via the official plugin:
@vue/composition-api
.Let's see the usage in step-by-step instructions,
Install the Plugin
npminstall @vue/composition-api#oryarnadd @vue/composition-api
Register the plugin in your
main.js
file,importVuefrom'vue';importVueCompositionAPIfrom'@vue/composition-api';Vue.use(VueCompositionAPI);newVue({render:h=>h(App),}).$mount('#app');
Using Composition API in ComponentsYou can now use
ref
,reactive
,computed
,watch
,onMounted
, etc., in your Vue 2 components.<template><div><p>Count:{{ count}}</p><button @click="increment">Increment</button></div></template><script>import{ ref}from'@vue/composition-api';exportdefault{setup(){constcount=ref(0);constincrement=()=>{count.value++;};return{ count, increment,};},};</script>
Note:
- The
@vue/composition-api
plugin is compatible withVue 2.6+. - It doesnot include all Vue 3 features (e.g.,
<script setup>
, Suspense, Fragments). - Great forgradual migration or using modern Vue patterns in Vue 2.
- The
TheComposition API in Vue 3 is a new way to organize and reuse logic in Vue components. It was introduced toaddress some limitations of theOptions API (used in Vue 2), especially when dealing with large or complexcomponents where logic can become scattered and hard to manage.
The Composition API is aset of APIs (e.g.,
setup()
,ref
,reactive
,computed
,watch
, etc.) that allowdevelopers tocompose component logic more flexibly and cleanly, especially when logic is reused acrosscomponents.The
**setup()**
functionThe heart of the Composition API. It's calledbefore the component is created, and it's the place where youdeclarereactive state, computed properties, methods, and lifecycle hooks.
<template><button @click="increment">Count is:{{ count}}</button></template><scriptsetup>import{ ref}from'vue'constcount=ref(0)functionincrement(){count.value++}</script>
ref()
creates areactive reference to a primitive value.count.value
is how you access/update the value inside aref
.
Reactive State:
**ref()**
and**reactive()**
- Used forprimitive values.
Wraps a value in a reactive object.
constmessage=ref('Hello');message.value='Hi';
- Used forobjects.
- Deeply reactive, making every property reactive.
constuser=reactive({name:'Alice',age:25});user.age++;
- Used forprimitive values.
Computed PropertiesCreates values that automatically update when dependencies change.
import{ref,computed}from'vue';setup(){constfirstName=ref('John');constlastName=ref('Doe');constfullName=computed(()=>`${firstName.value}${lastName.value}`);return{ fullName};}
Watchers
Both
watch()
andwatchEffect()
are used to run side effects when data changes:watch(count,(newVal,oldVal)=>{console.log('Count changed:',newVal);});watchEffect(()=>{console.log('Count is now',count.value);});
Lifecycle Hooks
Composition API uses new lifecycle hooks imported from Vue.
import{onMounted,onUnmounted}from'vue';onMounted(()=>{console.log('Component is mounted');});onUnmounted(()=>{console.log('Component is unmounted');});
Reusable Logic with Composables
You can encapsulate logic incomposable functions and reuse across components:
Example: Reuse logic of counter using
useCounter.js
import{ref}from'vue';exportfunctionuseCounter(initialValue=0){constcount=ref(initialValue);constincrement=()=>count.value++;constdecrement=()=>count.value--;constreset=()=>count.value=initialValue;return{ count, increment, decrement, reset};}
Using it in a component
<scriptsetup> import{useCounter} from '@/composables/useCounter'; const{count,increment,decrement,reset} = useCounter(5);</script><template><div><p>Count:{{ count}}</p><button @click="increment">+</button><button @click="decrement">-</button><button @click="reset">Reset</button></div></template>
The composition API provides several benefits over the traditional Options API as listed below.
- Better logic reuse and code organization:
- Better typescript support:
- Easier testing
- Smaller production bundles
Composition API is a set of additive, function-based APIs that allow flexible composition of component logic.
The
<Teleport>
component allows you to render a part of your component's template into a different location inthe DOM — outside the current component's hierarchy.- Wrap teleport component around the modal template inside
app.vue
file
<template><div><h1>Page Content</h1> <!-- This modal will render in the #modals element --><teleportto="#modals"><divclass="modal"><p>This is a modal rendered with teleport</p></div></teleport></div></template>
- Render modal component inside main
index.html
file
<body><divid="app"></div><divid="modals"></div> <!-- This is where the modal will appear --></body>
In Vue 2, this functionality was possible via the community plugin
portal-vue
- Modals
- Toasts
- Tooltips
- Context menus
- Dropdowns
- Anything that needs to break out of DOM nesting
- Wrap teleport component around the modal template inside
The
v-html
directive is used to update the inner html of a DOM element with latest data. It is similar toinnerHTML property in DOM.The example usage of this directive as shown below,
<divid="app"><div>{{ htmlContent}}</div><divv-html="htmlContent"></div></div>
Note: This directive should be used with trused content only but not on user provided content. Otherwise it canleads to XSS vulnerabilities.
Feature Vue 2 Vue 3 API Style Options API Composition API + Options API Reactivity System Object.defineProperty()
Proxy-based (faster & more reliable) Performance Moderate Improved (~2x faster in some cases) Tree-shaking Limited Fully tree-shakable TypeScript Support Partial Built-in and first-class support Fragment Support Not supported Supported Teleport/Portals Plugin-based Native support Suspense Not available Native support for async components The
watch()
function observes areactive source((likeref
,reactive
, getter functions or computed values)) andruns acallback function whenever that source changes.Syntax:
watch(source,callback,options ?)
- source: a ref, reactive property, getter function, or array of them.
- callback(newValue, oldValue): function to run when the value changes.
- options (optional): object to control behavior (e.g., immediate, deep).
import{ref,watch}from'vue';constcount=ref(0);watch(count,(newVal,oldVal)=>{console.log(`Count changed from${oldVal} to${newVal}`);});count.value=5;// triggers the watch callback
constfirstName=ref('Sudheer');constlastName=ref('Jonna');watch(()=>`${firstName.value}${lastName.value}`,(newFullName,oldFullName)=>{console.log(`Full name changed from${oldFullName} to${newFullName}`);});
import{reactive,watch}from'vue';constuser=reactive({name:'Sudheer',age:38});watch(()=>user,(newUser,oldUser)=>{console.log('User object changed:',newUser);},{deep:true});
The main uses of
watch()
are:- Watching route changes
- Triggering API calls
- Responding to complex data changes
- Manually reacting to specific state updates
InComposition API, lifecycle hooks are functions you callinside the
setup()
function to hook into differentstages of a component's lifecycle. These hooks replace the traditional options API lifecycle methods (likecreated()
,mounted()
, etc.) with function-based hooks that are imported from Vue.Lifecycle Stage Composition API Hook Description Before component creation onBeforeMount()
Called right before the component is mounted Component mounted onMounted()
Called after the component has been mounted Before update onBeforeUpdate()
Called before the component updates the DOM After update onUpdated()
Called after the component updates the DOM Before unmount onBeforeUnmount()
Called right before the component is unmounted Component unmounted onUnmounted()
Called after the component is unmounted Error captured onErrorCaptured()
Called when an error from a child component is captured Activated (keep-alive) onActivated()
Called when a kept-alive component is activated Deactivated (keep-alive) onDeactivated()
Called when a kept-alive component is deactivated The above hooks can be imported from
vue
and used insidesetup()
function. For example, the usage of hooks will beas follows,import{onMounted,onBeforeUnmount}from'vue'exportdefault{setup(){onMounted(()=>{console.log('Component is mounted!')})onBeforeUnmount(()=>{console.log('Component is about to unmount')})}}
Both watch and watchEffect are Composition API functions used for reactivity, but they serve different purposes andbehave differently.
watch: It is used to watch specific reactive sources (like ref or computed) and respond to changes. Thisfunction is lazy. i.e, The callback is not called immediately but only on changes. It is useful when you need toaccess both old and new values, and performs side-effects conditionally.
watch(count,(newVal,oldVal)=>{console.log('Count changed:',newVal);});
watchEffect: This function is automatic and eager. It runs a function immediately and tracks its dependenciesautomatically. It cannot access old values directly.
watchEffect(()=>{console.log(`Count is${count.value}`);});
About
List of 300 VueJS Interview Questions And Answers
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.