A Comprehensive Vue 2 to Vue 3 Migration Guide
Embracing Vue3’s new era
Welcome to the cutting-edge world of Vue.js!
The arrival of Vue 3 has ushered in a new era of enhancements and groundbreaking features. This evolutionary step from Vue 2 to Vue 3 promises developers a realm of opportunities to stay at the forefront of web development trends and harness the true potential of this remarkable framework.
In this comprehensive article, we'll take you through the seamless transition from Vue 2 to Vue 3, unveiling the magnificent advancements Vue 3 brings to the table. We’ll also cover how to handle the deprecated and eliminated parts during the migration process.
📄 Table of content
- What’s New in Vue 3
- Deprecated and Removed Items in Vue 3
- Step-by-Step Migration Guide: Transitioning Your Vue 2 App to Vue 3
1. Set the Foundation: Establish Vue 2 Project
2. Install Vue’s Migration Build
3. Fix Migration Build Errors
4. Resolve Package Compatibilities
5. Resolve Migration Build Warnings
6. Migrate to Vue 3’s Composition API
7. Test and Assure Your App’s Performance and Stability
8. Uninstall the Migration Build
What’s New in Vue 3
Composition API
Composition API provides a flexible way to organize component logic. This makes it easier to reuse code across components with better type checking. The Composition API is easier to maintain than the Options API.
While the Composition API was already available in Vue 2.7 through the officially maintained @vue/composition-api
plugin. In Vue 3, it is primarily used in conjunction with the <script setup>.
Better Type Support
Vue 3’s codebase is entirely written in TypeScript with auto-generated type definitions. It has better type inference and support for TypeScript’s optional chaining and null coalescing operators.
Improved Virtual DOM Algorithm
Virtual DOM has been completely redesigned in Vue 3. It utilizes a new diffing algorithm with compiler-based optimizations to speed up rendering.
Fragments, AKA Multiple Roots
In Vue 3, we can create multiple roots for a single component which was not feasible in Vue 2.
Vue 2 Syntax
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
Vue 3 Syntax: Components now can have multiple root nodes! However, this does require developers to explicitly define where attributes should be distributed.
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
Teleport
<Teleport>
is a built-in component that allows us to "teleport" a part of a component's template into a DOM node that exists outside the DOM hierarchy of that component.
<demo-component>
<teleport to="#teleport-area">
<pop-up />
</teleport>
</demo-component>
<div id="teleport-area"></div>
The to
target of <Teleport>
expects a CSS selector string or an actual DOM node. The teleport to
the target must be already in the DOM when the <Teleport>
component is mounted.
SFC State-driven CSS Variables (v-bind
in <style>
)
In Vue 3, style tags support linking CSS values to dynamic component state using the v-bind
CSS function.
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style>
.text {
color: v-bind(color);
}
</style>
Deprecated and Removed Items in Vue 3 ❌
Filters
In Vue 3, filters are removed. Instead, we can use methods or computed properties. You can refer below example.
In Vue 2:
<template>
<div>{{ name | uppercase }}</div>
</template>
<script>
export default {
data: function () {
return {
name: "milan",
};
},
filters: {
uppercase: function(value) {
return value.toUpperCase();
}
}
};
</script>
In Vue 3: Replaced filter with a computed property
<template>
<div>{{ uppercaseName }}</div>
</template>
<script>
export default {
data: function () {
return {
name: "milan",
};
},
computed: {
uppercaseName: function() {
return this.name.toUpperCase();
}
}
};
</script>
v-model
v-model
prop and event default names are changed:
- PROP:
value
⟶modelValue
- EVENT:
input
⟶update:modelValue
- BREAKING:
v-bind
's.sync
modifier and componentmodel
option is removed and replaced with an argument onv-model
- NEW: Multiple
v-model
bindings on the same component are possible now - NEW: Added the ability to create custom
v-model
modifiers.
v-model in Vue 2
<template>
<ChildComponent v-model="pageTitle" />
<!-- OR -->
<ChildComponent :value="pageTitle" @input="pageTitle=$event" />
</template>
v-model in Vue 3
<template>
<!-- Single v-model -->
<ChildComponent v-model="pageTitle" />
<!-- OR -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
<!-- Multiple v-model -->
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
<!-- OR -->
<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>
</template>
Event in Vue 2
<script lang="ts">
export default {
methods: {
updateTitle() {
this.$emit('input')
}
}
}
</script>
Event in Vue 3
<script lang="ts">
export default {
props: {
modelValue: String
},
emits: ['update:pageTitle'],
methods: {
changeTitle(title) {
this.$emit('update:pageTitle', title)
}
}
}
</script>
Inline templates
In Vue 2, the inline-template
attribute allowed child components to use their inner content as a template instead of distributing it.
However, Vue 3 no longer supports this feature. If you were using inline-template
in Vue 2, you can refactor the component using the default slot instead. This approach provides explicit data scoping while still allowing you to write child content inline for added convenience.
Named and Scoped Slots
In Vue 3, named and scoped slots have different syntaxes. To identify the slot name, use the v-slot directive or its # shorthand.
Slot in Vue 2
<template>
<div>
<template slot="heading" slot-scope="slotProps">
<h1>Slot ({{ slotProps.items.length }})</h1>
</template>
</div>
</template>
Slot in Vue 3
<template>
<div>
<template #heading="slotProps">
<h1>Slot ({{ slotProps.items.length }})</h1>
</template>
</div>
</template>
Global API Application Instance
Technically, Vue 2 doesn’t have a concept of an “app”. What we define as an app is simply a root Vue instance created via new Vue()
. Every root instance created from the same Vue constructor shares the same global configuration.
This global configuration poses challenges when using the same Vue instance for multiple “apps” on the same page with different global configurations.
// this affects both root instances
Vue.mixin({
/* ... */
})
const app1 = new Vue({ el: '#app-1' });
const app2 = new Vue({ el: '#app-2' });
To avoid these problems, Vue 3 introduces a new way to create Vue app — createApp.
const app = createApp(App);
// This will effect only 1 instance
app.mixin(/* ... */);
app.mount("#app");
Key Attribute
NEW: key
is no longer necessary on v-if
/v-else
/v-else-if
branches since Vue now automatically generates unique key
.
BREAKING: If you manually provide key
, each branch must use a unique key
.
BREAKING: <template v-for>
key
should be placed on the <template>
tag (rather than on its children).
v-if vs. v-for Precedence
In Vue 2: When using v-if
and v-for
on the same element, v-for
would take precedence.
In Vue 3: v-if
will always have higher precedence than v-for
There are few more minor changes in Vue3. Refer to this link for detailed info.
Step-by-Step Migration Guide: Transitioning Your Vue 2 App to Vue 3
Step 1: Set the Foundation: Establish Vue 2 Project
We’ll use one sample Vue 2 app to illustrate the migration procedure. You may clone this repository if you would like to practice with it as well.
Example: To do app with Vue2 — Vue 2 to Vue 3 migration
Step 2: Install Vue’s Migration Build
Before installing the migration build, you need to check a few things:
1. Update any deprecated named/scoped slot syntax to the latest version. Click here for the ref link.
2. If you are using a custom webpack, update the vue-loader to the latest version.
3. For vue-cli users, upgrade @vue/cli-service to the latest version.
Now, follow these steps:
- Update
vue
version to^3.1.0
- Install
@vue/compat,
also known as Vue’s migration build, with the same version as the updated one - Replace
vue-template-compiler
with@vue/compiler-sfc@^3.1.0
Example commit of package.json
"dependencies": {
- "vue": "^2.6.14",
+ "vue": "^3.1.0",
+ "@vue/compat": "^3.1.0"
...
},
"devDependencies": {
- "vue-template-compiler": "^2.6.14"
+ "@vue/compiler-sfc": "^3.1.0"
}
Then, configure the alias vue
to @vue/compat
and compiler options in *.config.js
as shown below.
For vue-cli:
// vue.config.js
module.exports = {
chainWebpack: (config) => {
config.resolve.alias.set('vue', '@vue/compat')
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => {
return {
...options,
compilerOptions: {
compatConfig: {
MODE: 2
}
}
}
})
}
}
For webpack:
// webpack.config.js
module.exports = {
resolve: {
alias: {
vue: '@vue/compat'
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
compatConfig: {
MODE: 2
}
}
}
}
]
}
}
For Vite:
// vite.config.js
export default {
resolve: {
alias: {
vue: '@vue/compat'
}
},
plugins: [
vue({
template: {
compilerOptions: {
compatConfig: {
MODE: 2
}
}
}
})
]
}
After switching to the migration build, you’ll encounter runtime warnings (in the browser console) and compiler warnings (in the command line). It’s best to address compiler warnings first, as some may break the application, such as the use of filters.
Example Commit : Click here
Note : Keep the
compatConfig
option until the full migration is complete.
Step 3: Fix Migration Build Errors
The Migration Build may not be fully compatible with Vue 3, so starting your application might not work. First, fix the deprecations.
Example Commit : Click here
Note: Refer to the Section 2: Deprecated and Removed Items in Vue 3 for more details on how to solve deprications.
Step 4: Resolve Package Compatibility
Many packages are now compatible with Vue 3. Update them to their latest versions. If you can’t find a Vue 3 compatible version for some packages, find suitable replacements.
Example Commit : Click here
Step 5: Resolve Migration Build Warnings
You may be able to execute your app now, but hold off on celebrating just yet. There’s still work to be done. When you run your application, you’ll encounter console warnings like the ones below.
These warnings need to be fixed.
Each warning has an identifier (e.g., GLOBAL_MOUNT, OPTIONS_DATA_FN, etc.). You can find a complete list of all the different warnings here.
Example Commit : Click here
Step 6: Migrate to Vue 3’s Composition API
This step is not necessary but recommended.
Example Commit : Click here
Step 7: Test and Assure Your App Performance and Stability
Thoroughly test your migrated components to ensure their functionality in the Vue 3 environment. Leverage the Vue Devtools extension to debug and inspect your application during migration.
Step 8: Uninstall the Migration Build
Once testing is complete and your application runs perfectly in Vue 3, it’s time to wrap things up!
- Uninstall @vue/compat package
- Remove the changes we made at the beginning of the article to vue.config.js.
Example Commit : Click here for link
📢 TADA!! Migration is completed. 🎉
Full Migration Example:
Vue 2 Todo App: main branch
Post Migration : vue3 branch
Adding to this,
Vue 2 support will end on December 31st, 2023.
To fully embrace Vue.js’ capabilities and the future of web development, developers like you must switch to Vue 3. This article covers Vue 3’s new features, removed and deprecated features, and step-by-step instructions for updating a sample Vue 2 application to Vue 3. Accept Vue 3 for a better development experience, cutting-edge web apps, and higher performance.
For more updates on the latest tools and technologies, follow the Simform Engineering blog.