After reading an Anthony Gore's article about using the new Composition API as some sort of replacement of Vuex, for smaller projects, I took a simple todo app I built in Codepen, and then I created a new Vue 3 app (using the vue cli) and lastly, I moved all the state and mutation methods from each component to one single file (global.js - which will be something like the store, in Vuex).
Source code and foreword
Here's a list of the source code and the Codepen I will refer to in this article:
Codepen: Vue To-do app
GitHub repo: todo-app-vue3
Netlify: https://relaxed-yonath-fa8475.netlify.app/
If you take a look at the todo app I created in Codepen you'll notice I'm not even using Vuex, I'm just using both props to pass data down to children and $emit to pass data/communicate up to parent components.
One of the advantages of the new Composition API is that now we have access to reactive features outside of components, which is quite powerful.
So here's what I did after creating my Vue 3 app, and putting the components code into its own files, and basically making the app work like it is working on Codepen:
Move the state and mutation functions to a global file
The first thing I did was to create the global.js file in /src
.
Inside global.js
, I imported the reactive
and the readonly
APIs. Let's take a look at the code in 'global.js' - I will add the comments in the code snippet.
import { reactive, readonly } from "vue";
/*
Wrapping our object with reactive() makes,
as it clearly suggests, our object reactive
(it even affects all nested properties).
*/
const state = reactive({
tasks: [
{
id: 1,
description: "Finish the course",
done: false,
},
{..},
{..},
{..},
{..}
],
nextId: 6,
tasksFiltered: [],
activeTab: "all",
})
/*
All these functions below are a combination of
mutations and actions (when comparing with Vuex).
But, of course, we are always free to organize our code
however we want.
*/
const filterTodos = function(filterOption) {..}
const addTodo = function(todo) {..}
const deleteTask = function(task) {..}
const toggleTaskStatus = function(task) {..}
// Export an object with the state and mutations
export default {
// With readonly(), we prevent our state to be mutated
// outside of the global.js module
state: readonly(state),
filterTodos,
addTodo,
deleteTask,
toggleTaskStatus
}
Use Provide / inject
Then, we need to make global.js
(our "custom store") accesible to all of the App.vue
child components. To do so, we have to use the provide
property inside our App.vue
in order to make global.js
available to all the child components, so we import global.js
in App
and then, we provide it.
Right after that, in each component, we need to inject
global
in order to use it on each of them.
Now a screenshot of each child component (but remember, you can always go to the repo and take a look a the code)
TodoList.vue
Form.vue
Header.vue
This approach can be improved, and could serve as a simpler alternative. Of course, Vuex is more debuggable and we can track mutations in the vue dev tools. So it will always depend on the project we're working on or our personal choice and point of view.
What do you think about this approach?
Do you have any suggestions?
Hope you found this article useful! 🙂
Top comments (10)
I've been arguing that Vuex provides little beyond the Composition API, depending on how much you use the Vuex differentiators: time travel, data access structure, and debugging.
To me, the main loss from Vuex is the structured/regulated access to data, and you have resolved that loss through your own structure above, providing a read-only state and specific methods for funneling all modifications. This makes debugging and tracing easy, when you need it. And it seems that I don't use the time travel at all, I just haven't needed it or even found it useful. It makes for an impressive demo, but benefit to me does not exceed the cost of the overhead boilerplate of Vuex, which I find makes it more difficult to debug (tracing or breakpointing Vuex dispatch vs debugging a modification function like those provided in your article).
Thank you for posting this, it is triggering a lot of thought in my mind for how I'd structure my Composition API state access; probably exactly the way you did it.
Thanks for your thorough comment about this Paul 😃.
And I'm glad it makes you think about how to structure Composition API in your projects.
Experimenting with these new features, reading your comments about it and trying to think on different approaches is quite exiting.
Cheers! ✌️
It is remind me of Context API in React
You are losing the debugging and time travel capabilities of vuex without really any benefit. Cool proof of concept anyway!
Absolutely, agree. That's clearly one of the caveats. One of the benefits I see when using this pattern in smaller projects is the fact that we don't have Vuex in our dependencies, and our projects can be even more lightweight.
Awesome! Nice approach! 🔥
Thanks David!! 🤘
Cool
😃 👍
Nice work. Thanks.
I guess we can also have module specific global-xx.js and inject as required.
I will try approach in my side project - as I don't really need time travelling, etc now.