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:
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 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:
<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:
<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.
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:
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:
- We can't pass a nested property of a
reactive
variable to a function. - We can't use destructuring.
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:
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 ?
// 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
.
<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.
<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:
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
:
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:
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. Nostate.count.value
needed. - If we update
count
variable thenstate.count
will be updated too ๐ณ. - If we update
state.count
thencount
will get updated too ๐ฒ. - So
state.count
andcount.value
are in sync. If we update one, the other will get updated too. But not always ๐ค. If we re-assign tostate.count
anotherref
then thecount
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:
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:
- 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?
Top comments (18)
The main reason I use Vue is for its automatic reactivity, unlike React where i have to set state and destruct objects into setState, it sucks. I love Vue's straight forward reactivity, it must be its biggest selling point... and single file components.
Composition API for me is stepping away from what makes coding simple... and you are right, the community will murder over what api you should use.
But come on... object.property.value... is this really what we are doing these days? That is a joke and a step backwards to me. If thats the case I might as well use React, theres no difference anymore, and Nextjs offers way more than Nuxtjs... except for and wait for it.... easy reactivity and global state.
So whats Vues selling points now?
Thanks for your comment @greggcbs. I strongly believe that Composition API is an awesome addition because is powerful and opens + fixes things that weren't possible with Options API.
I didn't like the push towards Composition API and actually discouraging Options API but I think that's a good thing because we move to one way of doing things with Vue, using script setup and composition api for creating components that is, because as you probably know, one confusing part of Vue.js is having so many ways of doing things.
I extensively use Composition API and the truth is that Options API is more intuitive and is easier for me to read the code whereas with Composition API I have to spend more time trying to find where reactivity variables are when they are split into logical concerns. Also, you will have hard time trying to split things into logical concerns. That's the truth but is also true, that code written with Composition API is more flexible, maintainable and scalable.
In the beginning, it was said that you can use Options API for creating components, and when those components become more advanced we can use composables instead of mixins to simplify/share code. I worked for a while with that hybrid approach of Options + Composition APIs and I really loved it but as I said, the community pushes towards Composition API and script setup and the Vue core teams knows better and are smarter than me, so I listened to them and now I am only using Composition API + script setup and Typescript because without TS, you can fall into reactivity loss easily even if you have many years that you work with Vue.
To summarize, the DX of Options API is better and with Composition API there is more responsibility to developers (that's not always a good thing) but I believe that the benefits Composition API give us justify everything and I think it's just a matter of time where everyone is used to it and some best patterns and practices arise and when that happens, no one will talk about Options API.
I strongly suggest you read the other article I wrote: How to use composition API the right way -> dev.to/the_one/are-you-using-compo...
Thanks for your in depth reply. Its been 8 months since our comments and after much debate and building products we have stepped away from Vue and Nuxt into React and Nextjs - specifically T3 App. For me and my team, this was the best decision we have made. We still have some small projects in Vue and its useful for that but Nuxt just didnt cut it for our large production project. Happy coding to everyone.
Very interesting thoughts and very reasonable indeed ๐ I specially like the emotional way you bring it up with ๐๐
And I can totally relate. I donโt use Vue on day-by-day basis now (unfortunately), mostly occasionally, and mostly for prototypes, pet projects and teaching my son coding. And the reason I like Vue is that itโs very comprehensive and straightforward. Itโs easy to explain web dev and programming with Vue even to a child. But all that is because of the options API. So deprecating it (if itโs on the map) is a really bad idea IMO.
Hi Fyodor thank you for your comment. Glad to see that you liked the article.
I would say that they "soft-deprecating" the Options API. In the docs is mentioned that they don't plan deprecating it but if you read the docs throughout you will see that Options API is discouraged.
๐ In Vue 2, Options api was fine for full application but now in Vue 3 they say that options api is for low-complexity apps or without using a bundler.
๐ They also say that for new projects choose Composition api only and using it, it's better performance wise because you "shave-off" some kilobytes.
๐ Also, Code written in Composition API and
script setup
is also more efficient and minification-friendly than Options API equivalent.So how can you win against the arguments to use CompA when you are in a team? You simply can't.
That's why I have switched totally to CompA.
If someone would ask me what is the best way to use Vue 3, I would say that it is Composition API with Typescript because I see that Vue team pushes us towards this way.
I feel something is wrong with the vue development team. They should think again why vue is so popular against react.
Options API is the reason that I choose vue against react. OptionsAPI is pretty intuitive and used in a straight way. Vue should stick to the options API, and provide composition as an option.
I totally agree with your regarding DX on using CompA. It is even worst when using CompA on Vue 2 project where we need to be aware all Vue 2 reactivity caveat. Things really get messy if i have array of big nested objects, and when doing v-for and want to process some of nested properties on an event by passing the nested property to event handler and trying to mutate the nested property. Normally i only use single ref on an array of nested objects. i never use nested reactive variable as i dont like the inconsistency auto unwrap behavior. the nested reactive is not fully documented in the document too. but i still like CompA especially in term of code organization. In my opinion, ref and reactive are quite messy. although i might handle the messiness, but it will be hard for any other junior dev on the same project
Thanks for you comment Omar.
A great article!
It is so real that I thought Options API is a Vue 2 thing and I recently decide to learn Composition API which I think it is a must if I want to write Vue 3 code.
For now, although I am still at the start of Composition API, I just love Options API. As what you said above, the code in real-world app doesn't look that clean. Especially when working in a project, which the code will be edited by different people, who have their own coding style. Although we have eslint, there will still be some minor difference.
My feeling to Composition API is that it looks a bit messy / unorganized, it will be hard to read and understand the code if it is long / unclean. So that in terms of maintenance, it may be a bit more trouble. However, Options API groups different parts of data / methods well, so that it gets a good structure which will be easier for reading the code and find what you need.
But yes, Composition API is obviously the trend and still good to learn it.
Hi @oliver139, thanks for your comment!
I highly recommend you read the next article: How to use Composition API the right way.
Choosing between Composition API and Options API is like choosing between render function or vue template. Render function is more powerful but it is not Vue. Most of the time templates do the job. Some devs may blame you for using vue templates, instead of render function and JSX but I will go with templates and options API and use render function and composition API for very rare cases. It is good to have choices.
@bkilinc thanks for your comment.
Options API, Composition API and render functions are all Vue. I don't understand why you say it is not Vue.
In Vue 3, the recommended way is to use Composition API with script setup and vue templates. Mostly because by having a template, Vue is able to do some optimizations.
Choosing between Composition API and Options API is definitely different compared to choosing render function or vue template. You can go with options API and use composition API in rare/advanced cases but the recommended way is to use only 1 way, it's discouraged to mix them both, and if you read the docs and also this article until the end, you will understand that Options API is actually discouraged for new projects.
Also, coding components with Options API is very different when coding them with Composition API in my 5+ years of experience using Vue.js. With the latter, you are forced to manually define reactivity and also you have to care about writing better code and use logical concerns.
I highly suggest you read my next article: How to use composition API the right way
Thanks. It is not uncommon that software products become bloated over time. For 2 years I used options API and no problems with it. I will stick with it. If a change is needed I may also look other frameworks. Svelte sounds cool, nowadays. But there is really no reason to change Vue / Options API for now. I will try composition API sometime. Many thanks.
@bkilinc thanks for your reply! I agree, I have been building Vue apps for years and never really had a problem with Options API even though I hated mixins.
When I first tried Vue 3 more than a year ago, I loved that instead of mixins, I could use Composition API (composables). It was early and docs weren't ready yet, so I intuitively went for a hybrid approach: Using Options API and when components became complex, I split the code by using composables.
I loved that way of coding, but when Vue 3 docs came out, I saw that the recommended way is Composition API and that Opt API is discouraged for new projects.
In the beginning I hated that but now I am used to it. I only use Composition API and I can say that it's truly powerful but I have seen many friends and colleagues write unreadable code with it. Composition API is not Options API and we should use it by grouping things in logical concerns. The responsibility is greater with Comp API on making sure to write readable code and also, having consistency on how to write components with it. In real world, code with Composition API can become really ugly if devs just throw code here and there.
Having said that, I don't think you should look for other frameworks. I have seen many devs switching off of Vue and hate on it, but the truth is that currently is the best framework out there and the fastest one. No one likes changes and the fact that there is more to learn, but in the end, I think it's worth it to code in that new way. What's also important, Composition API has unlocked possibilities that weren't possible before with Options API.
Vue 3 is awesome -- even though I believe pushing towards a hybrid approach would be better -- and the only thing that it lacks right now, is for the community to catch up. I plan to start making Youtube videos instead of just writing articles to showcase how great is Vue 3, and how to code with it so people can see the real value and actually like it. I am not good at it though (I suck at it), but soon, I will post some videos (Hopefully I don't get too much hate)
Ok, I have switched to Vue 3 a few weeks ago and enjoying many new features. Recently I liked the way that I can share state. In coming few weeks I may think different. But I will not mix different styles in my existing code, for sure.
Does that mean that there are is UX behavior that cannot be achieved with the Options API, or that you just have fewer tools at your disposal if you want to do something you didn't think of up front?
Like sometimes you really which you could 'goto' a particular line in code, and it would solve all of your (immediate) problems, but in fact, you're probably better off refactoring your code a bit so you don't need this 'hack'.
Well, speaking as a Vue dev who is working with Vue 3 after having used Vue 2 for a while, what I think the author means is that thanks to that "don't use OptA, use CompA!" suggestion and their constant insistence that CompA is now the "right" way to do Vue, there are a lot of libraries that only really work if you use CompA. That said, you CAN get around that with the whole "setup function" thing shown here, but not only do the Vue devs discourage that, it actually can make components more complex and exacerbates some of the issues mentioned here.
What I continue to shake my head at when it comes to all this is that the primary explanation given for the existence of the Composition API and why it's "better" is that the Options API "forces you to split concerns between data, computeds and methods" and that apparently angers some devs. But personally, I found a solution to that issue; it's called a CLASS. In Vue 2, you could grab vue-class-component and turn your Options API component into a class component and then data becomes properties, computeds become getters and methods become class methods... And suddenly you CAN group them all together the way you want! But the Vue team seems to have a burning hatred for Javascript/Typescript classes and have gone out of their way to make using them impossible... And I suspect BOTH decisions (screwing around with the internals to make using classes much harder AND this whole push for CompA) is because they really, really want Vue to be React. And the issue is, trying to make Vue React is driving developers away, because React devs will stick with React, and the decisions are alienating people who chose to use Vue in the first place... Because I'm betting most of them didn't choose Vue because they wanted React... Otherwise, why wouldn't they have chosen React in the first place?
I used to be a React dev, and when I was, I tried to avoid React hooks despite everyone and their mother hyping them up because every major project with hooks I saw was an unwieldy mess of deconstruction and random function calls that were near impossible to trace. I always preferred Vue because the heart didn't rely on this kind of bullshit, and how the devs seemed determined to make it so...