Vue: Pinia vs. Vuex
On the one hand, Pinia, the current official state management library for Vue, was developed for Vue 3 and takes advantage of its reactivity system, making it intuitive and simple.
On the other hand, Vuex, the previous official state management library, was developed for Vue 2 and uses the Flux pattern, proposed by Facebook and popularized by Redux.
Many projects were build using Vuex, so it is important to know how it works and how it differs from Pinia.
API & store structure
While Pinia prefers to use separated stores for each kind of data, Vuex uses a single store divided in modules for each kind of data.
Pinia | |
---|---|
Store A | Store B |
Vuex | ||
---|---|---|
Store
|
While each store of Pinia has a state, getters and actions, each module of Vuex have a state, getters, actions and mutations.
Pinia | |||||||
---|---|---|---|---|---|---|---|
Store A
|
Store B
|
Vuex | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
Store
|
Let’s see a code example of both.
Creating a store (setup) with Pinia
ref()
becomesstate
computed()
becomesgetter
function()
becomesaction
src/stores/todos.js
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
export const useTodosStore = defineStore('todos', function() {
const todos = ref([
{ id: 1, text: 'Learn Vue', done: true },
{ id: 2, text: 'Learn Pinia', done: false },
{ id: 3, text: 'Learn Vuex', done: false }
])
const allTodos = computed(function() {
return todos.value
})
function doTodo(todoId) {
todos.value = todos.value.reduce((todos, todo) => {
if (todo.id === todoId) {
todo = { ...todo, done: true }
}
todos.push(todo)
return todos
}, [])
}
return { todos, allTodos, doTodo }
})
src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
createApp(App)
.use(createPinia())
.mount('#app')
Accessing a Getter from the store with Pinia
src/components/TodoList.js
import { storeToRefs } from 'pinia'
import { useTodosStore } from '../stores/todos'
<script setup>
const { allTodos } = storeToRefs(useTodosStore())
</script>
Accessing an Action from the store with Pinia
src/components/Todo.js
import { useTodosStore } from '../stores/todos'
<script setup>
const { doTodo } = useTodosStore()
</script>
Creating a store with Vuex
src/store/index.js
import { createStore } from 'vuex'
export default createStore({
modules: {
todos: {
namespaced: true,
state: {
todos: [
{ id: 1, text: 'Learn Vue', done: true },
{ id: 2, text: 'Learn Pinia', done: false },
{ id: 3, text: 'Learn Vuex', done: false }
],
},
getters: {
allTodos(state) {
return state.todos
},
},
actions: {
doTodo(context, todoId) {
context.commit('doTodo', todoId)
}
},
mutations: {
doTodo(state, todoId) {
state.todos = state.todos.reduce((todos, todo) => {
if (todo.id === todoId) {
todo = { ...todo, done: true }
}
todos.push(todo)
return todos
}, [])
}
},
}
}
})
src/main.js
import { createApp } from 'vue'
import { useStore } from './store'
import App from './App.vue'
import store from './store'
createApp(App)
.use(store)
.mount('#app')
Accessing a Getter from the store with Vuex
src/components/TodoList.js
import { computed } from 'vue'
import { useStore } from 'vuex'
<script setup>
const todos = computed(function() {
return useStore().getters['todos/allTodos']
})
</script>
Accessing an Action from the store with Vuex
src/components/Todo.js
import { useStore } from 'vuex'
<script setup>
function doTodo(todoId) {
useStore().dispatch('todos/doTodo', todoId)
}
</script>
To learn more about them, we can consult the official documentation of Pinia and Vuex.
Conclusion
Defining and using a store with Pinia is simpler and more intuitive than doing it with Vuex. It also fits better with the reactivity of Vue 3 and the Composition API. Despite that, it is good to know how to work with Vuex because there are a lot of projects that were build with it and we could find ourselves in the situation of maintaining one of those projects.
Written by Samuel de Vega.