Upgrading Vue 2 to Vue 3: The Good, The Bad, and the Ugly

Introduction

The news is out: Vue 2 is coming to an end. End-of-life for Vue 2 is planned for December 31, 2023. If that looming date caused you to break out into a cold sweat, you are most likely not alone. The thought of having to take your well-established application and do a major upgrade to it can be unpleasant. We here at FYIN have partners with major projects that use Vue 2 and Vue 3 is a major update. The prospect of this upgrade can be a daunting task in light of busy schedules and heavy workloads. Imagine your other tasks disappearing into the ether so that you can focus solely on Vue updates. It’s a nice thought but not a realistic one. This is where we can help. 

Why?

System update 3d Vector concept. 3d glass morphism gear for mobile app design.

Why or why not? To upgrade or not? While there’s probably no need to turn it into a philosophical debate, it’s worth noting that not all upgrades involve a simple “click and done” process. Most upgrades involve time and resources. Software upgrades are like maintaining a house. You could choose not to do it but it will cause more effort and cost in the long run. 

So why upgrade Vue? Vue 2 will receive eighteen (18) months of long-term support starting July 1, 2022. There will be necessary bug fixes and security updates during that time but all updates will cease after that. Since we do not have a crystal ball to see the future, who knows what might happen? Over time, support for third-party packages for Vue 2 will decline and eventually end as well.

In addition to the loss of maintenance support, there are improvements in Vue 3 that make upgrading a good idea:

  • Composition API: A new API for writing more concise and reusable components.

  • TypeScript support: First-class support for TypeScript, making it easier to write type-safe code.

  • Performance improvements: Vue 3 is significantly faster than Vue 2, thanks to a number of optimizations.

  • New features: Vue 3 also includes a number of new features, such as Fragment and Teleport.

How to do it

Upgrading Vue is a straightforward process. To make the process even smoother, Vue has a migration build to help with the transition. The migration build is a special build that allows some of your existing Vue 2 apps to continue to work. This will give you warnings for deprecated code so that you can take your time to refactor those sections. There are still some breaking changes that will need to be addressed during the upgrade process, but the migration build helps to spread some of the work out over time. 

The general process will look something like this:

  1. Upgrade to Vue 3.

  2. Fix any errors.

  3. Fix any warnings.

  4. Uninstall the migration build.

Here is the process in more detail:

https://v3-migration.vuejs.org/migration-build.html#installation

  1. Upgrade tooling if applicable.

  • If using custom webpack setup: Upgrade vue-loader to ^16.0.0.

  • If using vue-cli: upgrade to the latest @vue/cli-service with vue upgrade

  • (Alternative) migrate to Vite + vite-plugin-vue2. [Example commit]

  1. In package.json, update vue to 3.1, install @vue/compat of the same version, and replace vue-template-compiler (if present) with @vue/compiler-sfc:

package. json

"dependencies": {

-  "vue": "^2.6.12",

+  "vue": "^3.1.0",

+  "@vue/compat": "^3.1.0"

   ...

},

"devDependencies": {

-  "vue-template-compiler": "^2.6.12"

+  "@vue/compiler-sfc": "^3.1.0"

   ...

}

  1. In the build setup, alias vue to @vue/compat and enable compat mode via Vue compiler options.

Example Configs

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

            }

          }

        }

      })

  }

}

 

Plain webpack

// webpack.config.js

module.exports = {

  resolve: {

    alias: {

      vue: '@vue/compat'

    }

  },

  module: {

    rules: [

      {

        test: /\.vue$/,

        loader: 'vue-loader',

        options: {

          compilerOptions: {

            compatConfig: {

              MODE: 2

            }

          }

        }

      }

    ]

  }

}

 

Vite

// vite.config.js

export default {

  resolve: {

    alias: {

      vue: '@vue/compat'

    }

  },

  plugins: [

    vue({

      template: {

        compilerOptions: {

          compatConfig: {

            MODE: 2

          }

        }

      }

    })

  ]

}

  1. If you are using TypeScript, you will also need to modify Vue's typing to expose the default export (which is no longer present in Vue 3) by adding a *.d.ts file with the following:

declare module 'vue' {

  import { CompatVue } from '@vue/runtime-dom'

  const Vue: CompatVue

  export default Vue

  export * from '@vue/runtime-dom'

  const { configureCompat } = Vue

  export { configureCompat }

}

  1. At this point, your application may encounter some compile-time errors/warnings (e.g. use of filters). Fix them first. If all compiler warnings are gone, you can also set the compiler to Vue 3 mode.

  2. After fixing the errors, the app should be able to run if it is not subject to the limitations mentioned above.

You will likely see a LOT of warnings from both the command line and the browser console. Here are some general tips:

  • You can filter for specific warnings in the browser console. It's a good idea to use the filter and focus on fixing one item at a time. You can also use negated filters like -GLOBAL_MOUNT.

  • You can suppress specific deprecations via compat configuration.

  • Some warnings may be caused by a dependency that you use (e.g. vue-router). You can check this from the warning's component trace or stack trace (expanded on click). Focus on fixing the warnings that originate from your own source code first.

  • If you are using vue-router, note <transition> and <keep-alive> will not work with <router-view> until you upgrade to vue-router v4.

  1. Update <transition> class names. This is the only feature that does not have a runtime warning. You can do a project-wide search for .*-enter and .*-leave CSS class names.

  2. Update app entry to use new global mounting API.

  1. Upgrade vuex to v4.

  1. Upgrade vue-router to v4. If you also use vuex-router-sync, you can replace it with a store getter.

After the upgrade, to use <transition> and <keep-alive> with <router-view> requires using the new scoped-slot based syntax.

  1. Pick off individual warnings. Note some features have conflicting behavior between Vue 2 and Vue 3 - for example, the render function API, or the functional component vs. async component change. To migrate to Vue 3 API without affecting the rest of the application, you can opt-in to Vue 3 behavior on a per-component basis with the compatConfig option.

  2. When all warnings are fixed, you can remove the migration build and switch to Vue 3 fully. Note: you may not be able to do so if you still have dependencies that rely on Vue 2 behavior.

 Vue Mastery has put together a nice cheat sheet to help with some of the breaking changes:

https://www.vuemastery.com/pdf/Migration-Guide-Cheat-Sheet.pdf

The vuejs.org site has very good documentation on the process if more details are needed.

https://v3-migration.vuejs.org/migration-build.html#installation 

 Vue CLI to Vite

Since you are here, why not migrate from the Vue CLI to Vite (https://vitejs.dev/)? The big reason to do this is speed. Vite is faster and who does not like to save time on application builds? We are not going to cover this process here, but we have found it to also be a fairly easy process. Vue School has a good article on the process. 

https://vueschool.io/articles/vuejs-tutorials/how-to-migrate-from-vue-cli-to-vite/

The Gotcha!

Fixing settings, cogwheels, adjusting sliders. Person changing, developing, modifying, repairing system.

Upgrading Vue is not that difficult. The “gotcha” in the process is with third-party packages. The size of the site as well as the type and number of third-party NPM packages will determine the complexity of a Vue upgrade as well as the level of effort needed to upgrade. While the Vue team has done a great job in making it easy to migrate to Vue 3, the same cannot be said for all of those “wonderful” NPM packages.

While it is not comprehensive, it is recommended that you review the “Know Limitations” document on the Vue 3 migration site:

https://v3-migration.vuejs.org/migration-build.html#known-limitations

Notably, Vuetify can be a lot of effort to upgrade. Vuetify 3 has many breaking changes and unfortunately, there is no equivalent tool like the Vue 3 migration build. If you are using Vuetify 2 in your application, be prepared to do a lot of view refactoring during your Vue 3 upgrade.  

https://vuetifyjs.com/en/getting-started/upgrade-guide/ 

Review your package and make sure that they have Vue 3 versions and what the process will be to upgrade them to work with Vue 3.

Conclusion

Upgrading development frameworks is not always easy, but not doing it can become a time-consuming and costly effort in the long run. While regular maintenance may seem costly now to both budget and timelines, it is ultimately easier and cheaper to do now than waiting and having to make a massive effort to squeeze multiple years of work into a compressed timeline. You don’t want to save money in the short term only to spend more later. 

Let's Talk Shop

Need to upgrade Vue 2 to Vue 3? Not sure and want some advice? We're here to help.