The Vue 2 → Vue 3 migration story!

More and more people are facing the choice, between staying with their Vue 2 application or migrating to Vue 3. Both are fine for the moment, but many considerations have to be weighed when deciding.
I assume most newly started projects will be using Vue 3. But this is the story and learnings from the situation where a migration from Vue 2 to 3 was decided.
Preface
Our application was (is) a medium size application with 10/15 routes. We are using external libraries to do charting, authorization, UI elements, etc… Below are my thoughts on migrating each element.
I won’t dive too much into why we decided to migrate (since you’re properly here because you already decided), so let’s just dive into how the migration of the different parts was planned and executed and what benefits that offered us.
UI framework — Vuetify
By far the most time-consuming task in our project was to migrate the UI library from a Vue 2 version to the corresponding Vue 3 version. In our case, we had to migrate Vuetify. As the Vuetify components were spread all across the application, we more or less had to migrate all the non-compatible components (some components, didn’t need any change). Luckily for us, an ESlint plugin has been created to help the migration process when using Vuetify.
Key takeaway In the Vue 3 version of our application, we decided to have a smaller set of base components (button, card, selector, etc), that we reuse all over the application. We should arguably have done that, to begin with, but now it would be possible to switch to another UI library much much easier.
Authentication — Auth0

For authentication, our application is using Auth0. This required us to do an update of the auth0 library. This is a crucial part of the application, even though it code-wise is not the biggest task. The logic was contained in one file, which made the migration relatively easy.
Key takeaway Authentication (and authorization) is a key element in many applications. Migrationwise, not much changed for this component and therefore it’s not so interesting migration-wise.
Store — Vuex

Using Vuex as the store in our Vue 2 application, left us with a choice. Should we switch to Pinia (as recommended by Vue) or stick with Vuex and just upgrade the version? Having some experience with Vue 3 and the composition API, we could see that changing state (and relying on component models) would be quite a bit easier and less code-heavy than in Vue 2.
For these reasons, we chose to only upgrade Vuex (less work) and at the same time minimize the use of global state whenever possible.
In time Vuex will be outfaced, but in reality, it turns out we didn’t need as much global state management as initially used in the Vue 2 application.
Key takeaway The main point here is that we now keep less global state, due to the ease of sharing data in Vue 3. Migration to Pinia will be in the pipeline, but for now, this specific element remains more or less unchanged, to avoid more migration than necessary.
Charts — ECharts / Chartjs
Unfortunately for our migration adventure, we had included two charting libraries in our application. This means we had to migrate two libraries. Luckily for us, the use of charting in the application was relatively limited, so for the first migration, we chose just to migrate the few components more or less 1-to-1.
Key takeaway
We found a system (Embeddable), that in the future will allow us to integrate charts as a native component (but built in another system), that will allow us to fully detach the charting code with the application code, which will allow us to migrate the two separately in the future.
Composition API
Not only do all the 3rd party libraries need to be updated, but we might as well update all our components to use the newest Composition API. If your application is sufficiently large, it might be wiser NOT to update to the newer composition API, and do that in a subsequent step.
Our application contains about 100 “components” (.vue files). This was a number that made us decide that we could rewrite all of them. Once you start, it becomes easier and easier, since a lot of it, is simply syntax change.
<template>
<div>
<v-chip class="mx-2 mb-2" dark variant="outlined" label color="info" size="small"
v-for="key in Object.keys(targeting)" :key="key">
{{ key }} = {{ targeting[key] }}
</v-chip>
<v-row class="mx-4 my-4 mb-2" :key="index" v-for="(adunit, index) in Object.keys(unitTargeting)">
{{ adunit }}:
<v-chip v-for="key in Object.keys(unitTargeting[adunit])" :key="key" class="mx-2 mb-2" dark variant="outlined"
label color="green" size="small">
{{ key }} = {{ unitTargeting[adunit][key] }}
</v-chip>
</v-row>
<v-row class="mx-2 mb-2" :key="index" v-for="(unit, index) in prebidTargeting">
{{ unit.unit }}
<v-chip class="mx-2 mb-2" dark variant="outlined" label color="orange" size="small"
v-for="key in Object.keys(unit.targeting)" :key="key">
{{ key }} = {{ unit.targeting[key] }}
</v-chip>
</v-row>
</div>
</template>
<script>
export default {
name: "targetingRow",
props: {
targeting: {},
unitTargeting: {
type: Object,
},
prebidTargeting: {
type: Array,
},
loading: {
type: Boolean,
},
},
async mounted() { },
data() {
return {};
},
methods: {},
computed: {},
};
</script>
<style lang="scss" scoped>
@import "@/styles.scss";
</style>With the composition API, the above script part ends up looking like this
<script setup>
defineProps({
targeting: {},
unitTargeting: {
type: Object,
},
prebidTargeting: {
type: Array,
},
loading: {
type: Boolean,
},
})
</script>In general, the Composition API seems more lean, with less code.
You will find yourself using this a lot less, which I think will feel nice for many people. Ref and reactive are concepts one needs to get used to, but this is like any other new syntax (takes some time).
Key takeaway The rewrite to the composition API has mainly gone without too many bumps and the result is much “better” components, with less clutter and boilerplate code. The main reason for the whole migration was not only to get the updated version of some 3rd party library but also to get all the benefits that Vue 3 offers (performance, better APIs, etc.)
Conclusion
Whether you should do a migration or stay with Vue 2 depends a lot on your application and your future development plan. In the long run, most libraries will be outdated and it will be extremely hard to make any changes to the application. So if the application is meant to be updated frequently over a longer period of time, I would say it's a reasonable safe bet to start the migration now.
After migration, the application is arguably in way better condition, due to refactoring and updating/deletion of old dependencies.
Overall the full migration took about 2–3 weeks!
You might also like





