Composition api or Options api?
Tags: vue, javascript, discuss
Published: 2022-06-17 18:28:17

## Clarification 🧼

I would like to note that this article is not expressing hate or anything towards Vue.js.
I LOVE Vue and I sponsor the development of it 💝. 
I just want to share my opinion, and I would love to hear your thoughts as well 👂.

## Options API is not Vue 2 🙅

First, I see a lot of people referring to OptA (Options API) as it is Vue 2 and that using it means you also have to use mixins and whatnot. That's not true.
I don't see why we distinguish between OptA (Options API) and CompA (Composition API). 
We can write CompA inside OptA, in the `setup` method:

```js
import { useCounter } from './my-composables'

export default {
  setup() {
    const [count, increment] = useCounter()

    return { count, increment }
  },

  data: () => ({
    username: null
  })
}
```

In fact, in the [Composition API RFC](https://github.com/vuejs/composition-api-rfc/blob/master/api.md) we saw just that. The introduction of a new component option inside OptA called `setup`.
So Options API is not Vue 2. You can use CompA api using the `setup` method, which means you can use composables instead of mixins.

## Composition api is powerful 💪

The first time I saw CompA I loved it! With it, we have more control about how we define reactivity, we can have reactivity outside of components and sharing code with composables is a great alternative to mixins.

The first time I tried the `setup` method along with OptA I thought to myself: "What a wonderful world" 🎶.

I replaced my mixins with composables, and I saw that CompA is really powerful and that it unlocks a lot of possibilities.
I could even extract and group code together that have the same logic, and I could even compose them together.
Composables look so clean, and they seem so simple compared to mixins.
Most importantly, you don't have to worry about the drawbacks of mixins.

### No one complained about the Options API 🤨

It's crystal clear that CompA is a great addition to Vue 3. With it, you can dig deeper and manually control reactivity:

```vue
<script>
import { ref, reactive } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const nonReactive = 'I am not reactive'
    const person = reactive({
      name: 'John'
    })
    
    return { count, person }
  }
}
</script>
```

Whereas with OptA reactivity works automatically:

```vue
<script>
export default {
  data: () => ({
    count: 0,
    person: {
      name: 'John'
    }
  })
}
</script>
```

So for me, it was like:

> Create components with Options API and in advanced cases use Composition API to better control reactivity. 

But no! OptA syntax is no longer recommended by Vue team even though I never heard anyone complaining about it.

## Options api for Todo apps? 😲

Vue docs say:

> For primarily low-complexity scenarios use OptA.
> For full applications, use CompA.

![img.png](https://pbs.twimg.com/media/FQpob-hXEAI6QSn?format=png&name=900x900)

What does this mean? Use **options api only for TODO apps?** ❓🤔

------------------------

Even though the `setup` method was introduced as a new component option, the docs recommend using it **only** if you have an existing OptA codebase. Because for new apps it's not recommended to use it:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kmf4ywgj8y2op7yhfpbr.png)

So it's clear! OptA is not recommended and from my experience on reading the docs, it's discouraged.

------------------------

### Be careful! 😨

If you say that you like OptA more, some devs will attack you!
If you say that you don't like `script setup` syntax, some devs will respond with:

> OptA is Legacy! You don't understand coding. You are a junior! Become a better dev just by using CompA.

## DX of the Composition API 🐛

Alright, since the Vue team & other devs push us to use CompA, we should drop OptA. Right? R i g h t ??
So let's see some CompA magic seen through my eyes.

### Why `ref` and `reactive` ??

`reactive` has some limitations:

1. We can't pass a nested property of a `reactive` variable to a function.
2. We can't use destructuring.

```js
const state = reactive({ count: 0 })

// the function receives a plain number and
// won't be able to track changes to state.count
callSomeFunction(state.count)

// count is a plain number that is disconnected
// from state.count.
let { count } = state
// does not affect original state
count++
```

And thus, Vue provides us `ref` to avoid the limitations of `reactive`. Vue docs say that by using `ref` we can avoid the limitations of `reactive` and gives us the following snippet:


```js
const obj = {
  foo: ref(1),
  bar: ref(2)
}

// the function receives a ref
// it needs to access the value via .value but it
// will retain the reactivity connection
callSomeFunction(obj.foo)

// still reactive
const { foo, bar } = obj
```

So `ref` comes as a solution. But does it solve the limitations of `reactive` ? Why not use the same example we had with `reactive` but by using `ref` to have a better comparison ?

```js
// const state = reactive({ count: 0 })
const state = ref({ count: 0 })

// callSomeFunction(state.count)
callSomeFunction(state.value)

// let { count } = state
let { count } = state.value
```

Hmm, that doesn't work either. So `ref` has the same limitations.
If you pull out a property of that object you will get a non-reactive property.

`ref` is a smart trick by providing an object that has a `value` property. While you use the ref as is you are good. If you mess with `.value` though, you might end up with the limitations of `reactive`.

### Usage in the template

You might wonder, how to display a `ref` variable in the template ? Should I access it via `.value` ?

The answer is no. Vue automatically "unwraps" the `ref` variable and displays the `.value`.

```html
<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  {{ count }}
  <button @click="count++">Increase counter</button>
</template>
```
Updating the `ref` variable in the template can be done without using `.value`, as you can see from the example above where we directly do `count++`.

### Ref unwrapping

So ref gets unwrapped when we use it in the template.
But not always:

- Automatic unwrapping of `ref` applies only to top-level properties.

```html
<script setup>
  import { ref } from 'vue'
  
  const object = { foo: ref(1) }
</script>

<template>
  {{ object.foo }} <!-- does NOT get unwrapped -->
</template>
``` 

- Automatic unwrapping of `ref` in reactive objects:

```js
const count = ref(0)
const state = reactive({ count })

console.log(state.count) // no need for .value
```
- No unwrapping in reactive arrays or native collections like `Map`:

```js
const books = reactive([ref('Vue 3 Guide')])
console.log(books[0].value) // need .value here

const map = reactive(new Map([['count', ref(0)]]))
console.log(map.get('count').value) // need .value here
```

### Ref syncing

Say that we have the 2 following variables:

```js
import { ref, reactive } from 'vue'

const count = ref(0)
const state = reactive({ count })
```

From the example above, here are some things that maybe don't work as you think they should work.

- `state.count` can be accessed and mutated directly. No `state.count.value` needed.
- If we update `count` variable then `state.count` will be updated too 😳.
- If we update `state.count` then `count` will get updated too 😲.
- So `state.count` and `count.value` are in sync. If we update one, the other will get updated too. But not always 🤔. If we re-assign to `state.count` another `ref` then the `count` will not be in sync anymore. 🤯

Whaaat 😳? Calm down, let's explain it.

So what happens, is that a `ref` inside a `reactive` object is getting unwrapped so no `.value` is available on the refs inside that object.
But remember that there is no unwrapping happening inside a `reactive` Array or a native collection type like Map or Set.

How about the `sync` part ? Why does that happen? And why does it stop only when assigning another `ref` ?

That's how it works. That can be powerful but with great power comes great responsibility.

----------------------------

A lot to keep in mind right? And we only touched on the `ref` and `reactive` APIs. But there is more:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qs18gb8shljsa5we573d.png)

### So what? 🤷

So, with Composition API:

- We have to decide what to use: ref or reactive? Believe me, there is no answer to that except that "it depends". Personally I mostly use `ref`.
- You have to manually define and make sure you don't lose reactivity. *As we learned above, ref and reactive limitations can easily lead to reactivity loss*
- You have to always keep in mind the limitations and the behaviors of how reactivity works. (unwrapping, syncing, .value, etc.)
- Many APIs like `toRef`, `toRefs`, `unref`, `isRef`, etc.

It's true that CompA is really powerful, but I believe that the points above show us that the DX is not that great compared to OptA.

### Conclusion (❁´◡`❁)

CompA is extremely powerful and has a lot of features, but it takes time to learn it and it can become difficult to debug especially if you don't use TypeScript.
Personally, I would say that the Development experience of OptA is much better than CompA for creating components.

You don't have to worry about reactivity loss or what apis to choose for reactivity and be careful with the limitations that come with them, but you focus on the component output.

Generally, I would say that using CompA for creating components has the following disadvantages:

- Unorganized/verbose code.

 I know that people show simple snippets where `script setup` looks cleaner, but the truth is that in real-world apps the code doesn't look that clean:

 ![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1641675666620/_XXbkoPcq.png)
- lots of lines of ungrouped code
- difficult to visually analyze the code (refs, functions, comments, etc. in one place)
- In order to make the code clean, devs usually add empty lines and comments (eg lifecycle hooks, watch etc) which is not ideal
- it is not clean and intuitive and makes it hard to search for specific sections (eg where is a specific method)
- enforce extraction. When a setup method becomes relatively big, since it has the above downsides, you feel the push to split it into composables. I've experienced that too many composables make it really difficult to debug and follow the flow of the code and also developers don't initially extract everything. Personally, I like building the component first, and then I do refactoring/cleanup code.

## My thoughts 💭

In the beginning of using Vue 3, I loved using OptA for creating components and CompA for things like sharing code, stores, etc. I thought that I had to use CompA for creating components **only** in advanced use cases just like I was using render-functions (only for advanced use cases). 

And I believe that if the Vue team would push towards this way of using Vue, things would be much better.
But from what I see, the recommendation is to use `script setup` and stick with CompA only.
And this is exactly what I did. I switched to using only CompA.

But, sometimes I work on a Vue 2 project for a part-time job and when I use OptA I feel more productive, and I love the facts that I don't have to manually define reactivity and that I can fold/unfold section of the code, and I visually analyze the code better (where is the method, where is the computed, etc.).

Even though now I am confident using CompA, there are still cases where I fall into reactivity loss or forget about reactivity apis behaviors (e.g. unwrapping, syncing, .value, etc.).

Haven't tried `script setup` with Typescript extensively yet, but I am experimenting with it, and it definitely feels great. I would dare to say that `script setup` was made to work only with Typescript because without it, development experience is not that great.


## Share your thoughts

All in all, CompA is great. OptA was great too.
Even though I loved OptA, I switched to CompA and probably is just a matter of time in order to get convinced that CompA is the way to go "for building full applications".

Or who knows? I might end up using OptA again despite the fact that docs discourage it. 😁

But what do you think? 🤔
Please share your thoughts. Let's discuss!

What do you prefer for creating components? Options or Composition API and why?