import { createApp } from 'vue'
createApp({
data() {
return {
count: 0
}
}
}).mount('#app')
<div id="app">
<button @click="count++">
Count is: {{ count }}
</button>
</div>​
Count is: 0
Vue (3.x)
only supports browsers with native ES2015 support. This excludes IE11. Vue 3.x
uses ES2015 features that cannot be polyfilled in legacy browsers, so if you need to support legacy browsers, you will need to use Vue 2.x
instead. v-if, v-else, v-else-if and v-show
isLoggedIn
is set to false.<button v-if="isLoggedIn">Logout</button>
<template>
element with the condition. For example, you can have both label and button together conditionally applied,<template v-if="isLoggedIn">
<label> Logout </button>
<button> Logout </button>
</template>
v-else
is used to display LogIn button
if isLoggedIn
is set to false(not logged in).<button v-if="isLoggedIn"> Logout </button>
<button v-else> Log In </button>
LogIn button
when ifLoginDisabled
property is set to true. This can be achieved through v-else statement
.<button v-if="isLoggedIn"> Logout </button>
<label v-else-if="isLoginDisabled"> User login disabled </label>
<button v-else> Log In </button>
<span v-show="user.name">Welcome user,{{user.name}}</span>
Index | v-show directive | v-if directive |
---|---|---|
1. | The v-if directive is used to render the element to the DOM only if the expression passes. | The v-show directive is used to render all elements to the DOM and then uses the CSS display property to show/hide elements according to the expression. |
2. | The v-if directive also supports v-else and v-else-if directives. | The v-show directive doesn't support the v-else and v-else-if directives. |
3. | The v-if directive has higher toggle costs since it adds or removes the DOM every time. | The v-show directive has higher initial render costs. |
4. | The v-if directive has the advantage when it comes to initial render time. | The v-show directive has a performance advantage if you have to switch on and switch off the elements frequently. |
5. | The v-if directive supports the tab. | The v-show directive doesn't support the tab. |
<ul id="list">
<li v-for="(item, index) in items">
{{ index }} - {{ item.message }}
</li>
</ul>
var vm = new Vue({
el: '#list',
data: {
items: [
{ message: 'John' },
{ message: 'Locke' }
]
}
})
<div id="object">
<div v-for="(value, key, index) of user">
{{ index }}. {{ key }}: {{ value }}
</div>
</div>
var vm = new Vue({
el: '#object',
data: {
user: {
firstName: 'John',
lastName: 'Locke',
age: 30
}
}
})
var vm = new Vue({
// options
})
$ yarn add vue-resource
$ npm install vue-resource
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.0/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.0"></script>
<script type="module">
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.7.0/dist/vue.esm.browser.js'
</script>
# latest stable
$ npm install vue
v-if directive
on <template>
element which works as invisible wrapper(no rendering) for group of elements.<template v-if="condition">
<h1>Name</h1>
<p>Address</p>
<p>Contact Details</p>
</template>
Vue.js
on parts of their projects and applications :<div id="app">
{{message}}
<input v-model="message">
</div>
<script type="text/javascript">
var message = 'Vue.js is rad';
new Vue({ el: '#app', data: { message } });
</script>
single pipe (|)
and can be followed by one or more arguments:<element directive="expression | filterId [args...]"></element>
Vue.filter()
method is used to create and register a custom filter in Vue js. Vue.filter()
method takes two parameters a filterId that is usnique name to filter that you going to create and a filter function that takes a value as the argument and returns the transformed value.Vue.filter('reverse', function (value) {
return value.split('').reverse().join('')
})
Vue.component('alert', {
template: `
<div class="alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
<alert>
There is an issue with in application.
</alert>
<template v-if="loginType === 'Admin'">
<label>Admin</label>
<input placeholder="Enter your ID">
</template>
<template v-else>
<label>Guest</label>
<input placeholder="Enter your name">
</template>
<template v-if="loginType === 'Admin'">
<label>Admin</label>
<input placeholder="Enter your ID" key="admin-id">
</template>
<template v-else>
<label>Guest</label>
<input placeholder="Enter your name" key="user-name">
</template>
v-for
iteration. An ideal value for key would be the unique id of each item.<div v-for="item in items" :key="item.id">
{{item.name}}
</div>
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
vm.todos.push({ message: 'Baz' })
filter()
concat()
slice()
vm.todos = vm.todos.filter(function (todo) {
return todo.status.match(/Completed/)
})
Vue.js
, every component instance has its own isolated scope. So, you cannot directly reference parent data in a child component's template.Vue.component('blog-post', {
// camelCase in JavaScript
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})​
VueX
is a state management pattern and library for the Vue.js application. It is used as a centralized store for all the different components in the Vue.js
application. Vuex
provides some rules to ensure that the state can only be mutated in a predictable fashion. You can get a lot of additional features by integrating Vuex
with the official devtool extension of Vue.js
. npm install vuex --save
(or)
yarn add vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
<script src="https://unpkg.com/vue.js"></script>
<script src="https://unpkg.com/vuex.js"></script>
const moduleOne = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleTwo = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const store = new Vuex.Store({
modules: {
one: moduleOne,
two: moduleTwo
}
})
store.state.one // -> `moduleOne's state
store.state.two // -> `moduleTwo's state
Vue.js
. One of the popular libraries is Axios. It is very simple to use and lightweight. You can include it in your project by using the following command.npm install axios --save
Implementing GET method using Axios in Vue JS
axios({ method: "GET", "URL": "https://httpbin.org/ip" }).then(result => {
this.ip = result.data.origin;
}, error => {
console.error(error);
});​
vue init webpack myproject
npm run build
.vue
file format.Vue.js
:<div id="app">
Name: <input type="text" v-model="name">
<button v-on:click="myClickHandler">Say Hello button</button>
</div>​
var myViewModel = new Vue({
el: '#app',
data: my Model,
// A click handler inside methods
methods: {
ClickHandler: function(e) {
alert("Hello " + this.name);
}
}
});
vm.todos[indexOfTodo] = newTodo
vm.todos.length = todosLength
set
and splice
methods, Let's see the solutions with an examples,// Vue.set
Vue.set(vm.todos, indexOfTodo, newTodoValue)
(or)
// Array.prototype.splice
vm.todos.splice(indexOfTodo, 1, newTodoValue)
vm.todos.splice(todosLength)
var vm = new Vue({
data: {
user: {
name: 'John'
}
}
})
// `vm.name` is now reactive
vm.user.email = john@email.com // `vm.user.email` is NOT reactive
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
})
.vue
extension that contains a Vue component. It contains the component’s template, logic, and styles all bundled together in one file. It consists of one <script>
block, optional <template>
and <style>
blocks, and possible additional custom blocks.Vue.js
:<button v-on:click="show('Welcome to VueJS world', $event)">
Submit
</button>
methods: {
show: function (message, event) {
// now we have access to the native event
if (event) event.preventDefault()
console.log(message);
}
}
event.preventDefault()
or event.stopPropagation()
inside event handlers. You can use methods provided by vue, but these methods are meant for data logic instead of dealing with DOM events. Vue provides below event modifiers for v-on and these modifiers are directive postfixes denoted by a dot..stop
.prevent
.capture
.self
.once
.passive
<!-- the click event's propagation will be stopped -->
<a v-on:click.stop="methodCall"></a>
<!-- modifiers can be chained -->
<a v-on:click.stop.prevent="doThat"></a>
v-on
for handling keyboard events. Let's take an example of keyup event with enter keycode.<!-- only call `vm.show()` when the `keyCode` is 13 -->
<input v-on:keyup.13="show">
.enter
.tab
.delete
(captures both “Delete” and “Backspace” keys).esc
.space
.up
.down
.left
.right
<input v-on:keyup.enter="submit" />
<!-- OR with shorthand notation -->
<input @keyup.enter="submit" />
config.keyCodes
. There are few guidelines for the propertiescamelCase
. Instead you can use kebab-case with double quotation marksVue.config.keyCodes = {
f1: 112,
"media-play-pause": 179,
down: [40, 87]
}​
.ctrl
.alt
.shift
.meta
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
VueJS 2.x
, every component must have a single root element when template has more than one element. In this case, you need to wrap the elements with a parent element.<template>
<div class="todo-item">
<h2>{{ title }}</h2>
<div v-html="content"></div>
</div>
</template>
3.x
, components now can have multiple root nodes. This way of adding multiple root nodes is called as fragments.<template>
<h2>{{ title }}</h2>
<div v-html="content"></div>
</template>
$emit
object to parent,Vue.component('todo-item', {
props: ['todo'],
template: `
<div class="todo-item">
<h3>{{ todo.title }}</h3>
<button v-on:click="$emit('increment-count', 1)">
Add
</button>
<div v-html="todo.description"></div>
</div>
`
})
<ul v-for="todo in todos">
<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>
v-model
. The <input>
inside the component must follow below rules,Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
/>
`
})
<custom-input v-model="searchInput"></custom-input>
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'
export default {
components: {
ComponentA,
ComponentB
},
// ...
}
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// The relative path of the components folder
'./components',
// Whether or not to look in subfolders
false,
// The regular expression used to match base component filenames
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// Get component config
const componentConfig = requireComponent(fileName)
// Get PascalCase name of component
const componentName = upperFirst(
camelCase(
// Strip the leading `./` and extension from the filename
fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
)
)
// Register component globally
Vue.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
)
})
Vue.js
, local registration
is required when the global registration seems not ideal. For example, suppose you are using a build system like Webpack and globally registering all components. In that case, even if we stop using a component, it could still be included in your final build. This unnecessarily increases the amount of JavaScript your users have to download. In these cases, it is better to define your components as plain JavaScript objects as follows:var ComponentA = {/*.......*/}
var ComponentB = {/*.......*/}
var ComponentC = {/*.......*/}
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentA
}
})
Vue.js
:<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" >
type="number"
, the value of HTML input elements always returns a string. That's why this typecast modifier is required.<input v-model.number="age" type="number"> ​
<input v-model.trim="msg">
Vue.js
, if you are using vue-router
, you should use router.go(path)
to navigate to any particular route. You can access the router from within a component using this.$router. router.go()
changed in Vue.js 2.0. You can use router.push({ name: "yourroutename"})
or just router.push("yourroutename")
now to redirect. props: ['defaultUser'],
data: function () {
return {
username: this.defaultUser
}
}
props: ['environment'],
computed: {
localEnvironment: function () {
return this.environment.trim().toUpperCase()
}
}
data-tooltip
attribute on the input then you can add this attribute to component instance,<custom-input data-tooltip="Enter your input" />
class
and style
are exception to this, these values will be merged in the child component.<!-- Child component -->
<input type="date" class="date-control">
<!-- Parent component -->
<custom-input class="custom-class" />
Vue.component('user-profile', {
props: {
// Basic type check (`null` matches any type)
age: Number,
// Multiple possible types
identityNumber: [String, Number],
// Required string
email: {
type: String,
required: true
},
// Number with a default value
minBalance: {
type: Number,
default: 10000
},
// Object with a default value
message: {
type: Object,
// Object or array defaults must be returned from
// a factory function
default: function () {
return { message: 'Welcome to Vue' }
}
},
// Custom validator function
location: {
validator: function (value) {
// The value must match one of these strings
return ['India', 'Singapore', 'Australia'].indexOf(value) !== -1
}
}
}
})
/user/john/post/123 and /user/jack/post/235
using dynamic segments,const User = {
template: '<div>User {{ $route.params.name }}, PostId: {{ route.params.postid }}</div>'
}
const router = new VueRouter({
routes: [
// dynamic segments start with a colon
{ path: '/user/:name/post/:postid', component: User }
]
})
Vue.js
, the Single File Components are used to solve the common problems in a JavaScript-driven application with a .vue
extension.Vue.js
:filters: {
capitalize: function (value) {
if (!value) return ''
valuevalue = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
} ​
Vue.filter('capitalize', function (value) {
if (!value) return ''
valuevalue = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
Vuex
application, creating a computed property every time whenever we want to access the store's state property or getter is going to be repetitive, difficult, and boring, especially if a component needs more than one state property. In this situation, we can use the mapState helper of vuex
, which generates computed getter functions for us.mapState
helper :// in full builds helpers are exposed as Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
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 used
greeting (state) {
return this.localTitle + state.username
}
})
}
computed: mapState([
// map this.username to store.state.username
'username'
])
Vue.js
, the $parent
property is used to access the parent instance from a child. It is similar to the $root
property. The $parent
property provides direct access, but it makes the application hard to test and debug. In this property, it is very difficult to find out where the mutation comes from. Vue.js
applications, memory leaks often come from using third-party libraries that create their own instances and/or manipulate the DOM. The v-if
directive and the Vue Router destroy Vue component instances. To overcome this issue, do a cleanup action before the component gets destroyed. It should be done manually in the beforeDestroy()
lifecycle hook.PowerGraph.js
, inside our component. It creates a graph instance that displays some data on the page :mounted() {
this.chart = new PowerGraph();
}
destroy()
method or implement our own cleanup method:beforeDestroy() {
this.chart.destroy();
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// UserProfile will be rendered inside User's <router-view> when /user/:id/profile is matched
path: 'profile',
component: UserProfile
},
{
// UserPosts will be rendered inside User's <router-view> when /user/:id/posts is matched
path: 'posts',
component: UserPosts
},
// UserHome will be rendered inside User's <router-view> when /user/:id is matched
{ path: '',
component: UserHome },
]
}
]
})
{{ message | filterA | filterB | filterB ... }}
pipe(|)
symbol. The first filter(filterA) takes the expression as a single argument and the result of the expression becomes an argument for second filter(filterB) and the chain continue for remaining filters.{{ birthday | dateFormat | uppercase }}
Vue
or apply an option to all Vue components available in our application. In this case, mixins
can be applied globally to affect all components in Vue. These mixins are called as global mixins
.Vue.mixin({
created(){
console.log("Write global mixins")
}
})
new Vue({
el: '#app'
})
global mixin
, the mixin options spread across all components with the console running during the instance creation. These are useful during test, and debugging or third party libraries. At the same time, You need to use these global mixins
sparsely and carefully, because it affects every single Vue instance created, including third party components./src/mixins
for ease of access. Once these mixins are created in a .js
file and exposed with the export keyword, they can be imported in any component with the import keyword and their file paths. <div v-avatar="{ width: 500, height: 400, url: 'path/logo', text: 'Iron Man' }"></div>
Vue.directive('avatar', function (el, binding) {
console.log(binding.value.width) // 500
console.log(binding.value.height) // 400
console.log(binding.value.url) // path/logo
console.log(binding.value.text) // "Iron Man"
})
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 |
Feature | VueJS | ReactJS |
---|---|---|
Type | JavaScript MVC 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) |
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) |
Written in | JavaScript | TypeScript |
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)
})
<my-component inline-template>
<div>
<h1>Inline templates</h1>
<p>Treated as component component owne content</p>
</div>
</my-component>
//ComponentA
<div>
<component-b >
</div>
//ComponentB
<div>
<component-a >
</div>
beforeCreate: function () {
this.$options.components.componentB = require('./component-b.vue').default
}
components: {
componentB: () => import('./component-b.vue')
}
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 |
module.exports = {
// ...
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
}
}
}
<template>
<div class="greeting">{{ message }}</div>
</template>
<script>
export default {
data () {
return {
message: 'Hello world for vueloader!'
}
}
}
</script>
<style>
.greeting {
color: blue;
}
</style>
/images/loader.png
)then it will be preserved as-is./images/loader.png
) then it will be interpreted as a relative module request and resolved based on the folder structure on your file system.~
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.@
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.<style scoped>
.greeting {
color: green;
}
</style>
<template>
<div class="greeting">Let's start Scoped CSS</div>
</template>
The above code will be converted to plain CSS,
<style scoped>
.greeting[data-v-f3f3eg9] {
color: green;
}
</style>
<template>
<div class="greeting" data-v-f3f3eg9>Let's start Scoped CSS</div>
</template>
// webpack.config.js
{
module: {
rules: [
// ... other rules omitted
{
test: /\.css$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
// enable CSS Modules
modules: true,
// customize generated class names
localIdentName: '[local]_[hash:base64:8]'
}
}
]
}
]
}
}
<style>
<style module>
.customStyle {
background: blue;
}
</style>
$style
<template>
<div :class="$style.blue">
Background color should be in blue
</p>
</template>
:class
binding.<style>
tags in a single *.vue
component.<style module="a">
/* identifiers injected as a */
</style>
<style module="b">
/* identifiers injected as b */
</style>
*.vue
files using either mocha-webpack or jeststore.hotUpdate()
API method is used for mutations and modules.vuex
store as below :// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import myModule from './modules/myModule'
Vue.use(Vuex)
const state = { message: "Welcome to hot reloading" }
const store = new Vuex.Store({
state,
mutations,
modules: {
moduleA: myModule
}
})
if (module.hot) {
// accept actions and mutations as hot modules
module.hot.accept(['./mutations', './modules/newMyModule'], () => {
// Get the updated modules
const newMutations = require('./mutations').default
const newMyModule = require('./modules/myModule').default
//swap in the new modules and mutations
store.hotUpdate({
mutations: newMutations,
modules: {
moduleA: newMyModule
}
})
})
}
import Vuex from "vuex";
Vue.use(Vuex)
// Make sure to call Vue.use(Vuex) first if using a module system
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
store.commit('increment')
console.log(store.state.count) // -> 1
const store = new Vuex.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 => {
return state.todos.filter(todo => todo.completed)
}
}
})
import { mapGetters } from 'vuex'
export default {
computed: {
// mix the getters into computed with object spread operator
...mapGetters([
'completedTodos',
'todosCount',
// ...
])
}
}
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 handler signature).
mutations: {
increment (state, payload) {
state.count += payload.value
}
}
# use preset from GitHub repo
vue create --preset username/repo my-project
let version = Number(Vue.version.split('.')[0])
if (version === 2) {
// Vue v2.x.x
} else if (version === 1) {
// Vue v1.x.x
} else {
// Unsupported versions of Vue
}
nextTick
method is just a comfortable way to execute a function after the data has been set, and the DOM has been updated. As an example, the usage is going to be similar to setTimeout
:// modify data
vm.msg = 'Welcome to Vue'
// DOM not updated yet
Vue.nextTick(function () {
// DOM updated
})
// usage as a promise (2.1.0+)
Vue.nextTick()
.then(function () {
// DOM updated
})
//i18n-setup.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '@/lang/en'
import axios from 'axios'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: 'en', // set locale
fallbackLocale: 'en',
messages // set locale messages
})
const loadedLanguages = ['en'] // our default language that is preloaded
function setI18nLanguage (lang) {
i18n.locale = lang
axios.defaults.headers.common['Accept-Language'] = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguageAsync (lang) {
if (i18n.locale !== lang) {
if (!loadedLanguages.includes(lang)) {
return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => {
i18n.setLocaleMessage(lang, msgs.default)
loadedLanguages.push(lang)
return setI18nLanguage(lang)
})
}
return Promise.resolve(setI18nLanguage(lang))
}
return Promise.resolve(lang)
}
loadLanguageAsync
function can be used inside a vue-router beforeEach hook.router.beforeEach((to, from, next) => {
const lang = to.params.lang
loadLanguageAsync(lang).then(() => next())
})
npm install Vuetify
import Vue from 'vue'
import Vuetify from 'vuetify' // Import Vuetify to your project
Vue.use(Vuetify) // Add Vuetify as a plugin