Composition api or Options api?
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.countcallSomeFunction(state.count)// count is a plain number that is disconnected// from state.count.let { count } = state// does not affect original statecount++
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 connectioncallSomeFunction(obj.foo)// still reactiveconst { 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 } = statelet { 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 hereconst 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?
Original Link: https://dev.to/the_one/composition-api-or-options-api-59gf
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To