Tutorial Vue.js To-Do con Vuex: Guía paso a paso para desarrolladores principiantes e intermedios
Construir una aplicación To-Do es un excelente ejercicio para aprender y dominar tecnologías frontend modernas. En este tutorial aprenderás a crear una aplicación de tareas (To-Do) utilizando Vue.js para la interfaz y Vuex para la gestión del estado global. Además, implementaremos almacenamiento local gracias a localStorage para mantener las tareas incluso al recargar la página.
Este tutorial está dirigido a desarrolladores principiantes e intermedios interesados en el desarrollo frontend con Vue.js. Cada sección incluye explicaciones claras y bloques de código para facilitar la comprensión.
Índice
- 1. Instalación y configuración del entorno
- 2. Estructura inicial del proyecto Vue
- 3. Creación de componentes Vue para la aplicación To-Do
- 4. Configuración y uso de Vuex para gestión de estado
- 5. Funcionalidades: agregar, editar, eliminar y marcar tareas como completadas
- 6. Integración de almacenamiento local con localStorage
- 7. Buenas prácticas y recomendaciones finales
1. Instalación y configuración del entorno
Para este tutorial asumimos que tienes Node.js instalado en tu sistema. Si no lo tienes, descárgalo e instálalo desde nodejs.org.
Paso 1: Crear un proyecto Vue con Vue CLI
Vue CLI facilita la configuración inicial.
En tu terminal:
1 2 3 |
npm install -g @vue/cli vue create vuex-todo-app |
Durante la creación, selecciona las opciones manuales para incluir Vuex, y elige Vue 3 si te interesa la versión más actualizada. Puedes usar configuraciones predeterminadas si prefieres.
Paso 2: Ingresar al proyecto e instalar dependencias adicionales
1 2 3 |
cd vuex-todo-app npm run serve |
Esto levanta un servidor local en http://localhost:8080/
. Sirve para verificar que la instalación haya sido exitosa.
2. Estructura inicial del proyecto Vue
La estructura resultante tendrá al menos estos archivos claves:
src/main.js
: Punto de entrada de la aplicación.src/App.vue
: Componente raíz.src/store/index.js
: Archivo para configurar Vuex.
Para trabajar cómodamente, crearemos una carpeta para nuestros componentes:
1 2 3 4 5 6 |
src/ └── components/ ├── TodoList.vue ├── TodoItem.vue └── TodoInput.vue |
TodoList.vue
: Lista que muestra todas las tareas.TodoItem.vue
: Componente individual de tarea.TodoInput.vue
: Formulario para agregar nuevas tareas.
3. Creación de componentes Vue para la aplicación To-Do
3.1 Componente TodoInput.vue
Este componente será el formulario para agregar nuevos ítems.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<template> <form @submit.prevent="submitTodo"> <input v-model="newTodo" type="text" placeholder="Agregar nueva tarea" required /> <button type="submit">Agregar</button> </form> </template> <script> export default { name: 'TodoInput', data() { return { newTodo: '' }; }, methods: { submitTodo() { this.$emit('add-todo', this.newTodo); this.newTodo = ''; } } }; </script> <style scoped> form { display: flex; gap: 0.5rem; } input { flex-grow: 1; padding: 0.5rem; } button { padding: 0.5rem 1rem; } </style> |
3.2 Componente TodoItem.vue
Representa cada tarea, con opciones para marcar completada, editar y eliminar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
<template> <li :class="{ completed: todo.completed }"> <input type="checkbox" :checked="todo.completed" @change="$emit('toggle-completion', todo.id)" /> <span v-if="!isEditing" @dblclick="startEditing"> {{ todo.text }} </span> <input v-else v-model="editText" @keyup.enter="finishEditing" @blur="finishEditing" type="text" autofocus /> <button @click="$emit('delete-todo', todo.id)">Eliminar</button> </li> </template> <script> export default { name: 'TodoItem', props: { todo: { type: Object, required: true } }, data() { return { isEditing: false, editText: this.todo.text }; }, methods: { startEditing() { this.isEditing = true; this.editText = this.todo.text; }, finishEditing() { if (this.editText.trim()) { this.$emit('edit-todo', { id: this.todo.id, text: this.editText }); } this.isEditing = false; } } }; </script> <style scoped> li { display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0; } .completed span { text-decoration: line-through; color: gray; } input[type="text"] { flex-grow: 1; padding: 0.2rem 0.5rem; } button { background-color: #e74c3c; border: none; color: white; cursor: pointer; padding: 0.25rem 0.5rem; } button:hover { background-color: #c0392b; } </style> |
3.3 Componente TodoList.vue
Esta lista renderiza todos los elementos To-Do a partir del estado que maneja Vuex.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<template> <ul> <TodoItem v-for="todo in todos" :key="todo.id" :todo="todo" @toggle-completion="toggleCompletion" @edit-todo="editTodo" @delete-todo="deleteTodo" /> </ul> </template> <script> import TodoItem from './TodoItem.vue'; export default { name: 'TodoList', components: { TodoItem }, props: { todos: { type: Array, required: true } }, methods: { toggleCompletion(id) { this.$emit('toggle-completion', id); }, editTodo(payload) { this.$emit('edit-todo', payload); }, deleteTodo(id) { this.$emit('delete-todo', id); } } }; </script> <style scoped> ul { list-style: none; padding: 0; } </style> |
4. Configuración y uso de Vuex para gestión de estado
4.1 Crear el store Vuex
Edita src/store/index.js
para manejar el estado global de las tareas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
import { createStore } from 'vuex'; export default createStore({ state: { todos: [] }, mutations: { setTodos(state, todos) { state.todos = todos; }, addTodo(state, todo) { state.todos.push(todo); }, toggleTodo(state, id) { const todo = state.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; }, editTodo(state, payload) { const todo = state.todos.find(t => t.id === payload.id); if (todo) todo.text = payload.text; }, deleteTodo(state, id) { state.todos = state.todos.filter(t => t.id !== id); } }, actions: { loadTodos({ commit }) { const todos = JSON.parse(localStorage.getItem('vuex-todos')) || []; commit('setTodos', todos); }, saveTodos({ state }) { localStorage.setItem('vuex-todos', JSON.stringify(state.todos)); }, addTodo({ commit, dispatch }, text) { const todo = { id: Date.now(), text, completed: false }; commit('addTodo', todo); dispatch('saveTodos'); }, toggleTodo({ commit, dispatch }, id) { commit('toggleTodo', id); dispatch('saveTodos'); }, editTodo({ commit, dispatch }, payload) { commit('editTodo', payload); dispatch('saveTodos'); }, deleteTodo({ commit, dispatch }, id) { commit('deleteTodo', id); dispatch('saveTodos'); } }, getters: { allTodos(state) { return state.todos; } } }); |
4.2 Integrar el store en la aplicación
En src/main.js
importa y usa el store:
1 2 3 4 5 6 7 8 |
import { createApp } from 'vue'; import App from './App.vue'; import store from './store'; createApp(App) .use(store) .mount('#app'); |
5. Funcionalidades: agregar, editar, eliminar y marcar tareas como completadas
Ahora unimos todo en el componente raíz App.vue
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<template> <div id="app"> <h1>Aplicación To-Do con Vue.js y Vuex</h1> <TodoInput @add-todo="addTodo" /> <TodoList :todos="todos" @toggle-completion="toggleCompletion" @edit-todo="editTodo" @delete-todo="deleteTodo" /> </div> </template> <script> import { mapActions, mapGetters } from 'vuex'; import TodoInput from './components/TodoInput.vue'; import TodoList from './components/TodoList.vue'; export default { name: 'App', components: { TodoInput, TodoList }, computed: { ...mapGetters(['allTodos']), todos() { return this.allTodos; } }, methods: { ...mapActions(['addTodo', 'toggleTodo', 'editTodo', 'deleteTodo', 'loadTodos']), toggleCompletion(id) { this.toggleTodo(id); }, editTodo(payload) { this.editTodo(payload); }, deleteTodo(id) { this.deleteTodo(id); } }, created() { this.loadTodos(); } }; </script> <style> #app { max-width: 600px; margin: 2rem auto; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } h1 { text-align: center; margin-bottom: 1rem; } </style> |
Explicación
- Importamos los componentes y Vuex.
- Usamos
mapGetters
para traer las tareas desde el store Vuex. - A través de
mapActions
vinculamos las acciones para modificar el estado global. - Capturamos eventos personalizados emitidos desde los componentes hijos para interactuar con el store.
- En el hook
created()
, cargamos las tareas guardadas enlocalStorage
.
6. Integración de almacenamiento local con localStorage
La funcionalidad para almacenar tareas en localStorage
está implementada directamente en las acciones Vuex:
loadTodos
recupera el listado desdelocalStorage
cuando la app inicia.saveTodos
actualizalocalStorage
con el estado actual cada vez que se modifica la lista.
Este enfoque asegura persistencia entre recargas sin depender de servidores externos.
7. Buenas prácticas y recomendaciones finales
- Modulariza el código separando componentes para mantener la claridad.
- Usa el patrón Flux a través de Vuex para disentir la lógica de estado y componentes visuales.
- Agrega validación básica para evitar enviar tareas vacías.
- Utiliza el guardado en localStorage solo para prototipos o aplicaciones pequeñas; considera backend para apps más complejas.
- Usa el evento
@blur
y la edición con teclado para mejorar UX al editar tareas. - Para mejorar SEO y accesibilidad, asigna etiquetas semánticas y atributos aria donde sea necesario.
Conclusión
¡Felicidades! Ahora tienes una aplicación To-Do funcional utilizando Vue.js y Vuex con almacenamiento local. Este proyecto es una base sólida para profundizar en conceptos avanzados de desarrollo frontend Vue.js y gestión estado Vuex.
Te animamos a extender la aplicación agregando filtros por estado (tareas completas/incompletas), fechas límite o integrando APIs externas.
Si deseas aprovechar aprendizaje práctico con ejemplos claros y optimizados para SEO, no olvides practicar y compartir.
¿Listo para llevar tu aplicación Vue al siguiente nivel? Comienza a modificar tu To-Do, experimenta con Vuex y explora más características del ecosistema Vue.js.