Migrate components written in Vue2 Options API to Vue3 Composition API

Olimpiu Șeulean
5 min readSep 18, 2022

--

If you’re going to migrate your app/s to Vue3, or just trying the new syntax of Vue3 the upcoming topic will help you kickstart with a lot of confidence.

Vue3 Composition API image

First things first, why is this update important?

Vue3 is coming up with a new paradigm -> Composition API. The main advantage of the Composition API is that it enables clean, efficient logic reused in the form of Composable functions. It solves all the drawbacks of mixins, the primary reasoning reuses a mechanism for Options API. Moreover, more flexible code organization: Many users love that we write organized code by default with Options API: everything has its place based on the option it falls under. However, Options API poses severe limitations when a single component’s logic grows beyond a certain complexity threshold. This limitation is particularly prominent in components that deal with multiple logical concerns, which we have witnessed first-hand in many production Vue2 apps.

Official documentation: https://vuejs.org/guide/extras/composition-api-faq.html#better-logic-reuse

In short, Vue3 will be the new default in a few months.

It’s safe to continue using Vue2 for a while since it will continue to get security upgrades/bug-fixing and so on, until the end of 2023. Currently, Vue2 has entered maintenance mode. But isn’t this a great opportunity for us to migrate any ongoing projects we may have?

Vite — is a modern, blazing-fast tool for scaffolding and bundling projects. Well, even though this is one of my favorite subjects, during my component migration article from Vue2 to Vue3 I will try to keep it brief.

Vite is a new breed of frontend build tooling that significantly improves the frontend development experience. It is created by Evan You, the author of Vue himself!

Official documentation: https://vitejs.dev/guide/

Vue2 Component — Options API

I will start writing a basic Vue2 Options API component, and after that, I will rewrite it step by step in Vue3 Composition API.

Things to keep in mind that will be refactored later with Vue3:
1) Child component declaration
2) Async component declaration
3) Declaring props, emits
4) Declaring reactive data, methods, computed, watcher, event emitting
5) Lifecycle hooks
6) Mixins

Composition API

This topic also deserves a separate article on its own, since it’s a big feature of Vue3.

The Composition API was added in Vue3 as an alternative mechanism to the Options API for creating component states and logic. It’s basically a set of APIs that allow us to create Vue components by importing functions rather than defining options. It is an umbrella term that covers the following APIs:

  • Reactivity API, e.g. ref() and reactive(), allows us to directly create the reactive state, computed state, and watchers.
  • Lifecycle Hooks, e.g. onMounted() and onUpdated(), that allow us to hook into the component lifecycle programmatically.
  • Dependency Injection, i.e. provide() and inject(), allows us to leverage Vue’s dependency injection system while using Reactivity APIs.

Composition API is a built-in feature of Vue3 and is currently available to Vue2 via the officially maintained @vue/composition-api plugin. In Vue3, it’s also primarily used together with the <script setup> syntax in Single-File Components (SFCs).

Official documentation: https://vuejs.org/guide/extras/composition-api-faq.html#what-is-composition-api

Vue3 with Composition API

Let’s take each part of our reference SFC and adapt it, step by step, to this new paradigm.

Reactive data declaration

Reactive data are declared with the ref and reactive helpers.

· ref can take primitives (most common: Boolean , String and Number) as arguments as well as Objects;

· reactive can take Objects as arguments.

Another difference here is that with ref() it has a .value property that you have to use to get its content, but with reactive() you can access it directly:

Helpful link: Ref vs Reactive

Define child components

Making a component available in the template, it’s working in the same way as in Vue2. See below the importing. For the async components, in Vue3 setup() the paradigm is different, you will need to import a helper from ‘vue’ (defineAsyncComponent).

Computed properties

For computed properties, we will need to use another import. Notice the absence of this keyword inside the computed function. With the way <script setup> works, it’s no longer needed.

Watchers

Watch helper accepts the variable and a callback function. Beware that watching complex types like objects is done deeply and can have a negative performance impact. You can watch only one property of an object by passing it as a second parameter. Read more about the new watch helper in the official documentation.

Lifecycle hooks

Life cycle hooks function similarly in Vue2 and Vue3 but with Composition API, access to these life cycle hooks has changed as follows.

Vue2 vs Vue3 life cycle hooks

beforeCreate() and created() lifecycle hooks are not needed since setup() is replacing them and performing the same functionality. And it’s important that these life cycle hooks are imported before using them. See example:

Methods

As we already mentioned, every function declared inside the <script setup> will be available in the template.

Event emitting

To emit an event, first, we need to declare it with the defineEmits helper and then use the return value as the emitter.

Props

Defining component props can be done similarly. All the validation options are still supported.

NOTE: defineProps and defineEmits are compiler macros only usable inside <script setup>. They do not need to be imported.

Mixin usage

The root of all evil* and the primary reason Composition API was introduced is to eliminate the usage of mixins. Declaring and exporting a reactive variable and a function from a file is an option, but an even better one is using a composable.

Imported and used like this:

*Personal and unpopular opinion: Mixins might not be the best way to reuse code, but they don’t deserve all the hate they recently get.

Component Name

A tiny detail is missing. Our component has a name, and declaring it inside the script setup is not possible. Thankfully multiple script tags are supported in the same file, which is the solution according to the original RFC.

Vue template and SCSS/CSS

No changes are required for the Vue template and the style section for the component. Good news, isn’t it? Let’s put everything together to see the output.

Vue3 Component — Composition API

At first glance, our component looks different, but in reality, the functionality is still the same. Everything is explicitly imported or declared, eliminating namespace collisions. Component API is clearly defined, and common functionality can be grouped together if needed.

Vue3 Composition API (left) vs Vue2 Options API (right)

That’s all so far. What are your thoughts on Composition API? Is our component cleaner and more maintainable? Leave a comment to get your thoughts.

--

--