Migrating a Vue 2 project to Vue 3
Tags: vue, javascript, webdev
Published: 2021-11-24 10:36:59

In September of 2020 Evan You announced the stable release of Vue 3. [The official blog post](https://blog.vuejs.org/posts/vue-3-one-piece.html) stated that it comes with

> improved performance, smaller bundle sizes, better TypeScript integration, new APIs for tackling large scale use cases, and a solid foundation for long-term future iterations of the framework.

But should you take the plunge immediately and migrate all your Vue 2 projects to Vue 3? Well, it depends. For many of you, the answer is probably _‘not yet’_, for others it’s a wholehearted _‘yes’_.

Vue 3 doesn’t introduce anything revolutionary that you [can’t already do in some fashion in Vue 2](https://itnext.io/yes-vue-3-is-out-but-you-probably-dont-need-it-%EF%B8%8F-3e60634991b4). But some features might be sufficiently attractive to upgrade. Let’s go through the four main features of Vue 3, and then you can make an informed decision yourself.

First a disclaimer. Coming from a React background, Vue is still quite new to me, and one of the reasons for embarking on the migration journey was to learn more about the inner workings. So don’t expect any deep analysis of advanced new features, and take everything with a grain of salt.

## New features
### Layered internal modules
This is basically a fancy way of saying that they’ve **reduced tight coupling** between internal parts of Vue, making them more modular and swappable. This can also result in a smaller final bundle size, since parts of the Vue runtime may be completely or partly removed during tree shaking.

These new modules also expose some of their internal APIs, which makes it possible to do some advanced solutions. One example of this is to write a custom renderer (in the blog post they mention Native or integrated systems), and the normal DOM renderer is also using these APIs.

### Performance improvements
According to their own blog post, Vue 3 is up to **41% smaller**, renders up to **55% faster**, updates up to **133% faster** and uses up to **54% less memory**. However, in the migration of our product we found negligible bundle size decreases, and in some cases increases. We did not perform any speed performance testing, but my general standpoint is to trust the developers. Our project is so small from the beginning, so perhaps there wasn’t room for much savings. 

### Improved TypeScript integration
Vue 3 has been **written completely in TypeScript**, this means that all internal methods have type definitions bundled. For us developers, this means mainly that you will get some better autocompletion in VS Code, but also that moving your project to Typescript will be easier. 

### Composition API
Perhaps the largest change is the Composition API, which makes it possible to **couple related data, methods, watches, and computations together** instead of splitting the concerns over multiple parts of the component. This can make for a more elegant code base, as well as interesting possibilities for reusing parts of the logic in multiple components. See [the introduction article in the Vue documentation](https://v3.vuejs.org/guide/composition-api-introduction.html) for more information. But basically, it means we can go from this:

![A Vue component with mixed concerns in data, computed and methods](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d6g8jxtsxf3cy2zd2ab.png) 

to this:

![A Vue component using setup, with concerns next to each other](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b60iel0gvqo22ss0kbms.png) 

and then refactored to this:

![The same Vue component refactored to be brief](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yl6m3w5zca17e05jbogv.png) 

## Migration pattern
The complexity in migrating an existing Vue 2 project to Vue 3 entirely depends on what external plugins and libraries you are using, and how well they support Vue 3. Vue provides a migration build to ease the process, allowing you to migrate in steps while keeping some backwards compatibility. They also provide nice [step-by-step instructions](https://v3.vuejs.org/guide/migration/migration-build.html) on how to migrate. I won’t go through it in detail here but will mention some pain points I encountered when migrating.

### Notable breaking changes
#### Global API changes
Where you could previously access the global `Vue` object to, for example, add some global variables to `Vue.prototype`, you now have to do add it to the config object of the actual Vue instance you’re using, i.e. `app.config.globalProperties`.

#### `V-model` on custom components
When using `v-model` on custom components, the incoming data and emitted events have been changed. They can now be accessed on `modelValue` and `update:modelValue`. This opens up for custom components to have multiple `v-model` bindings.

#### Functional components
Functional components are now only created via a plain function and the functional attribute on single-file components have been deprecated. Where you previously used functional components to gain some performance increases, this gain is now negligible and the recommended way is to move all components to stateful components.

#### Render function changes
If you’re using the render function in any of your components instead of normal template components. The syntax for this has now changed.

### Migration steps
The steps to complete migration can be found in the documentation, but mainly consists of:

1. Update Vue and add compatibility package

1. Fix warnings exposed by the compatibility package

1. Update mounting of your app

1. Upgrade `vuex`, `vue-router`, and any other Vue plugins you’re using

1. When all warnings are fixed, uninstall the compatibility package

Overall the migration is easy to follow, and all official plugins (`vuex`, `vue-router`, etc) have good migration guides as well. We noted that we used `node-sass`, which has been deprecated and replaced by sass, but that was an easy swap. We couldn’t go past v10 of `sass-loader`, since it requires Webpack 5, which is a migration for another day.

One package we used for automatic formatting of input fields, `vue-mask`, seemed abandoned when we migrated, but seems to be updated to Vue 3 now. But keep an eye out for packages that doesn’t support Vue 3.

### Why is it not `@latest`?
If you’ve looked into Vue 3, you might have found that (at the time of writing) the `@latest` tag on NPM still points to Vue 2, with Vue 3 being relegated to `@next` still. Even though Vue 3 was released in 2020, they are holding up on pointing everyone to the latest version until more plugins have been migrated to support Vue 3. Since Vue 3 contains a number of breaking changes, if you rely on a plugin that hasn’t migrated, you’re still stuck on Vue 2. 

The communication from Vue on this subject could be more transparent, however. Their roadmaps and blogposts are at this point quite outdated.

## Conclusion
### Should you take the plunge?
It depends. If you’re not using any of the deprecated features extensively, then I would absolutely recommend upgrading. Especially if you’re looking to moving to Typescript, the process will be easier if you’re on Vue 3. All official packages and dev tools support Vue 3, so if you use a fairly vanilla setup, the process will probably be smooth.

If you use some packages that don’t support Vue 3, and you can’t find alternatives (as we did), you will have to stay on Vue 2. That being said, if you didn’t feel like any of the new features seem interesting to you, you’re fine being on Vue 2. 

But as with all projects, staying up to date has its perks. As time goes on, more and more packages will migrate to Vue 3, and eventually drop support for Vue 2, at which point you’re forced to upgrade for the same reason as you were forced to wait before.