Intro
In this article we are going to explore why we should use Composition API and how to do it properly.
Why not the old Options API way?
First of all, even though the Options API is not deprecated, reading through the docs you will see that it is discouraged and the new recommended way is Composition API and script setup.
From the docs:
Go with Options API if you are not using build tools, or plan to use Vue primarily in low-complexity scenarios, e.g. progressive enhancement.
Go with Composition API + Single-File Components if you plan to build full applications with Vue.
If you intend to exclusively use Composition API (along with the options listed above), you can shave a few kbs off your production bundle via a compile-time flag that drops Options API related code from Vue
Options API does allow you to "think less" when writing component code, which is why many users love it. However, in reducing the mental overhead, it also locks you into the prescribed code organization pattern with no escape hatch, which can make it difficult to refactor or improve code quality in larger scale projects. In this regard, Composition API provides better long term scalability.
So in short, using Composition API instead of Options API gives you performance benefits, provides better long term scalability and is the recommended way if you plan to build full applications.
Why Composition API is indeed better?
Now that we understood that Composition API is better and is the recommended way to write components, let's try to understand why.
With Options API we could have a code like this:
export default {
data: () => ({
count: 0,
newUser: ''
newTodo: ''
}),
computed: {
filteredTodos(){ /*...*/ },
filteredUsers(){ /*...*/ }
},
methods: {
doubleCount(){ /*...*/ },
addAndClearUser(){ /*...*/ },
addAndClearTodo(){ /*...*/ }
}
}
This component deals with 3 logical concerns: count, user, and todo. At a first glance we don't see an issue because the component is very small but if we add more to that component, we will see that the logical concerns are forced to be split under different options, located in different parts of the file.
Now that we know the issue, let's see how we can solve it with the Composition API:
import { ref, computed } from 'vue'
// Count
const count = ref(0)
function doubleCount(){ /*...*/ }
// User
const newUser = ref('')
const filteredUsers = computed(() => { /*...*/ })
function addAndClearUser(){ /*...*/ }
// Todo
const newTodo = ref('')
const filteredTodos = computed(() => { /*...*/ })
function addAndClearTodo(){ /*...*/ }
Notice how the code related to the same logical concern can now be grouped together: we no longer need to jump between different options blocks while working on a specific logical concern.
So now with Composition API we don't have to scroll up and down to find the code related to a specific logical concern.
What's even more exciting is that we can move a group of code into an external file with minimal effort, since we no longer need to shuffle the code around (like with Options API) in order to extract them. So if we were to do that our component would look like this:
import { useCount, useUser, useTodo } from './composables'
const { count, doubleCount } = useCount()
const { newUser, filteredUsers, addAndClearUser } = useUser()
const { newTodo, filteredTodos, addAndClearTodo } = useTodo()
Amazing isn't it? 😎🆒
How to structure logical concerns?
So while the key takeaway from Composition API is that we need to group code related to a specific logical concern together, I have experienced that the code can be hard to read and understand if there is no consistency.
So we need to organize a logical concern in a consistent way.
Personally, I organize the logical concerns as if I were to write it with Options API. So in the following order:
<script setup>
// Props (defineProps)
// Emits (defineEmits)
// Reactive variables (inc composables)
// Computed
// Methods
// Watchers
// Lifecycle Hooks
// Expose (defineExpose)
</script>
Let me know in the comments if you use a different way of organizing a logical concern.
By using this consistent way of organizing logical concerns, the code is easier to read and understand.
Another pattern I have seen, is adding a comment before each logical concern to differentiate between them. Personally, I use this pattern and it has helped me reading the code easier so I highly recommend it.
For example:
<script setup>
import { ref } from 'vue'
// Count 👈 this
const count = ref(0)
function doubleCount(){ /*...*/ }
// User 👈 this
const newUser = ref('')
function addAndClearUser(){ /*...*/ }
</script>
Lastly, I always prefer function
declaration over const
(arrow functions) for functions. That has resulted a good pattern and one reason is that you won't get Uncaught ReferenceError: Cannot access '<variable>' before initialization
😉.
Conclusion
I hope this article helped you to understand why Composition API is a better way to write components and how to use it properly. In the beginning you might not immediately see the benefits, but as your application grows, you will thank yourself for using it.
Composition API has unlocked a lot of possibilities and it enables clean and efficient logic reuse. It's just a matter of time falling in love with it but you have to use it the right way and so far in my experience I have seen many devs using it the wrong way.
Thanks for reading this article and make sure to leave a like and share with your network if you liked it. 🎉
Top comments (8)
Glad you posted this. Just starting to move a bunch of options API to composition and this has become super clear now.
Glad to see that you found it helpful. I highly recommend you read the other article 7 Vue3 tips you need to know. Hope it can be helpful 😊
Thank you, Roland! Love your code organizing method.
Glad to see that you like it, Thank you
Quite interesting,
but a question arises:
Why not keep using Options API, if you are simulating the same pattern?
Even though I am working on a solution (it's taking too much time though)
You can't use Options API to group logical concerns. I am pretty sure you just read the article fast without actually understanding it.
I suggest you take the time to read it and I am sure you will understand what a logical concern is, why Composition Api allows for that and if you use Composition Api for a long time, you will see that the code becomes unmaintainable and difficult to read if you and you team don't use a consistent way of creating components with Composition Api.
Having all the freedom to throw/write functions and variables in the component, usually ends up with a mess if you don't have some guidelines on how to structure things inside a component. One thing I love about Options API, and even today I find myself very productive when I work on a Vue 2 project, is that I know exactly where things are and it enforces people to have a structure of how they define functions and variables but also by automatically enabling reactivity without you needing to specify it (it is a ref, shallowRef, readonly/computed, etc)
I hope this comment answers your question and if you have tips/suggestions of how to better structure logical concerns or in general write components with Composition API, please share them.
Thank you 🙏
Hi, thanks for your answer.
I read it again, and you were right, I was missing some points,
you even explicitly said "as if I were to write it with Options API",
but the key point is you were talking about one single logical concern,
and moved to a separate file.
Thanks for your kindness and patience.
Glad to see that you better understood the article.
I hope it was useful.
Likewise, Thank you for your kindness and patience 🙏