¿Qué viene a VueX?
Publicado: 2022-03-10Vuex es la solución para la gestión de estado en aplicaciones Vue. La próxima versión, Vuex 4, está dando los pasos finales antes de su lanzamiento oficial. Esta versión brindará compatibilidad total con Vue 3, pero no agrega nuevas funciones. Si bien Vuex siempre ha sido una solución poderosa y la primera opción para muchos desarrolladores para la administración de estado en Vue, algunos desarrolladores esperaban que se abordaran más problemas de flujo de trabajo. Sin embargo, incluso cuando Vuex 4 acaba de salir, Kia King Ishii (un miembro del equipo central de Vue) está hablando sobre sus planes para Vuex 5, y estoy tan emocionado por lo que vi que tenía que compartirlo con ustedes. todos. Tenga en cuenta que los planes de Vuex 5 no están finalizados, por lo que algunas cosas pueden cambiar antes del lanzamiento de Vuex 5, pero si termina siendo similar a lo que ve en este artículo, debería ser una gran mejora para la experiencia del desarrollador.
Con la llegada de Vue 3 y su API de composición, la gente ha estado buscando alternativas simples hechas a mano. Por ejemplo, You Might Not Need Vuex demuestra un patrón relativamente simple, pero flexible y robusto para usar la API de composición junto con provide/inject
para crear almacenes de estado compartidos. Sin embargo, como dice Gabor en su artículo, esta (y otras alternativas) solo deben usarse en aplicaciones más pequeñas porque carecen de todas esas cosas que no están directamente relacionadas con el código: soporte de la comunidad, documentación, convenciones, buenas integraciones de Nuxt y desarrollador. herramientas.
Ese último siempre ha sido uno de los mayores problemas para mí. La extensión del navegador Vue devtools siempre ha sido una herramienta increíble para depurar y desarrollar aplicaciones Vue, y perder el inspector de Vuex con "viajes en el tiempo" sería una gran pérdida para depurar cualquier aplicación no trivial.
Afortunadamente, con Vuex 5 podremos tener nuestro pastel y comérnoslo también. Funcionará más como estas alternativas de API de composición, pero conservará todos los beneficios de usar una biblioteca de administración estatal oficial. Ahora echemos un vistazo a lo que va a cambiar.
Definición de una tienda
Antes de que podamos hacer algo con una tienda Vuex, debemos definir una. En Vuex 4, una definición de tienda se verá así:
import { createStore } from 'vuex' export const counterStore = createStore({ state: { count: 0 }, getters: { double (state) { return state.count * 2 } }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
Cada tienda tiene cuatro partes: el state
almacena los datos, los getters
le dan el estado calculado, las mutations
se usan para cambiar el estado y actions
son los métodos que se llaman desde fuera de la tienda para lograr cualquier cosa relacionada con la tienda. Por lo general, las acciones no solo cometen una mutación como muestra este ejemplo. En cambio, se utilizan para realizar tareas asincrónicas porque las mutaciones deben ser sincrónicas o simplemente implementan una funcionalidad más complicada o de varios pasos. Las acciones tampoco pueden mutar el estado por sí solas; deben usar un mutador. Entonces, ¿cómo se ve Vuex 5?
import { defineStore } from 'vuex' export const counterStore = defineStore({ name: 'counter', state() { return { count: 0 } }, getters: { double () { return this.count * 2 } }, actions: { increment () { this.count++ } } })
Hay algunos cambios a tener en cuenta aquí. Primero, en lugar de createStore
, usamos defineStore
. Esta diferencia es insignificante, pero está ahí por razones semánticas, que veremos más adelante. A continuación, debemos proporcionar un name
para la tienda, que no necesitábamos antes. En el pasado, los módulos tenían su propio nombre, pero no los proporcionaba el propio módulo; eran solo el nombre de la propiedad que les asignó la tienda principal que los agregó. Ahora, no hay módulos . En cambio, cada módulo será una tienda separada y tendrá un nombre. Este nombre es utilizado por el registro de Vuex, del que hablaremos más adelante.
Después de eso, necesitamos hacer que state
sea una función que devuelva el estado inicial en lugar de simplemente establecerlo en el estado inicial. Esto es similar a la opción de data
en los componentes. Escribimos getters
de manera muy similar a como lo hicimos en Vuex 4, pero en lugar de usar el state
como parámetro para cada getter, puede usar this
para llegar al estado. De la misma manera, actions
no necesitan preocuparse de que se pase un objeto de context
: simplemente pueden usar this
para acceder a todo. Finalmente, no hay mutations
. En cambio, las mutaciones se combinan con actions
. Kia notó que, con demasiada frecuencia, las mutaciones se convertían en simples setters, haciéndolas innecesariamente detalladas, por lo que las eliminaron. No mencionó si estaba "bien" mutar el estado directamente desde fuera de la tienda, pero definitivamente se nos permite y anima a mutar el estado directamente desde una acción y el patrón Flux frunce el ceño ante la mutación directa del estado.
Nota : Para aquellos que prefieren la API de composición sobre la API de opciones para crear componentes, les alegrará saber que también hay una manera de crear tiendas de manera similar a usar la API de composición.
import { ref, computed } from 'vue' import { defineStore } from 'vuex' export const counterStore = defineStore('counter', { const count = ref(0) const double = computed(() => count.value * 2) function increment () { count.value++ } return { count, double, increment } })
Como se muestra arriba, el nombre se pasa como el primer argumento para defineStore
. El resto parece una función de composición para componentes. Esto producirá exactamente el mismo resultado que el ejemplo anterior que usó la API de opciones.
Obtener la instancia de la tienda
En Vuex 4, las cosas han cambiado desde Vuex 3, pero solo miraré v4 para evitar que las cosas se salgan de control. En v4, cuando llamaste a createStore
, ya lo instanciaste. Luego puede usarlo en su aplicación, ya sea a través app.use
o directamente:
import { createApp } from 'vue' import App from './App.vue' // Your root component import store from './store' // The store definition from earlier const app = createApp(App) app.use(store) app.mount('#app') // Now all your components can access it via `this.$store` // Or you can use in composition components with `useStore()` // ----------------------------------------------- // Or use directly... this is generally discouraged import store from './store' store.state.count // -> 0 store.commit('increment') store.dispatch('increment') store.getters.double // -> 4
Esto es algo que Vuex 5 hace un poco más complicado que en v4. Cada aplicación ahora puede obtener una instancia separada de Vuex, lo que garantiza que cada aplicación pueda tener instancias separadas de las mismas tiendas sin compartir datos entre ellas. Puede compartir una instancia de Vuex si desea compartir instancias de tiendas entre aplicaciones.
import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' // Your root component const app = createApp(App) const vuex = createVuex() // create instance of Vuex app.use(vuex) // use the instance app.mount('#app')
Ahora todos sus componentes tienen acceso a la instancia de Vuex. En lugar de dar la definición de su(s) tienda(s) directamente, luego las importa a los componentes en los que desea usarlas y usa la instancia de Vuex para instanciarlas y registrarlas:
import { defineComponent } from 'vue' import store from './store' export default defineComponent({ name: 'App', computed: { counter () { return this.$vuex.store(store) } } })
Al llamar $vuex.store
, instancia y registra la tienda en la instancia de Vuex. A partir de ese momento, cada vez que use $vuex.store
en esa tienda, le devolverá la tienda ya instanciada en lugar de volver a instanciarla. Puede llamar al método de store
directamente en una instancia de Vuex creada por createVuex()
.
Ahora se puede acceder a su tienda en ese componente a través this.counter
. Si está utilizando la API de composición para su componente, puede usar useStore
en lugar de this.$vuex.store
:
import { defineComponent } from 'vue' import { useStore } from 'vuex' // import useStore import store from './store' export default defineComponent({ setup () { const counter = useStore(store) return { counter } } })
Importar la tienda directamente al componente e instanciarlo allí tiene ventajas y desventajas. Le permite dividir el código y cargar la tienda de forma perezosa solo donde se necesita, pero ahora es una dependencia directa en lugar de ser inyectada por un padre (sin mencionar que necesita importarla cada vez que quiera usarla). Si desea usar la inyección de dependencia para proporcionarla en toda la aplicación, especialmente si sabe que se usará en la raíz de la aplicación donde la división del código no ayudará, entonces puede usar provide
:
import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' import store from './store' const app = createApp(App) const vuex = createVuex() app.use(vuex) app.provide('store', store) // provide the store to all components app.mount('#app')
Y puede simplemente inyectarlo en cualquier componente donde lo vaya a usar:
import { defineComponent } from 'vue' export default defineComponent({ name: 'App', inject: ['store'] }) // Or with Composition API import { defineComponent, inject } from 'vue' export default defineComponent({ setup () { const store = inject('store') return { store } } })
No estoy entusiasmado con esta verbosidad extra, pero es más explícito y más flexible, de lo cual soy fanático. Este tipo de código generalmente se escribe una vez al comienzo del proyecto y luego no vuelve a molestarlo, aunque ahora deberá proporcionar cada nueva tienda o importarlo cada vez que desee usarlo, pero importar o inyectar módulos de código es la forma en que generalmente tenemos que trabajar con cualquier otra cosa, por lo que solo está haciendo que Vuex funcione más en la línea de cómo la gente ya tiende a trabajar.
usando una tienda
Además de ser un fanático de la flexibilidad y la nueva forma de definir las tiendas de la misma manera que un componente que usa la API de composición, hay una cosa más que me emociona más que todo lo demás: cómo se usan las tiendas. Esto es lo que parece usar una tienda en Vuex 4.
store.state.count // Access State store.getters.double // Access Getters store.commit('increment') // Mutate State store.dispatch('increment') // Run Actions
State
, los getters
, mutations
y actions
se manejan de diferentes maneras a través de diferentes propiedades o métodos. Esto tiene la ventaja de la claridad, que elogié antes, pero esta claridad realmente no nos aporta nada. Y esta API solo se vuelve más difícil de usar cuando usa módulos con espacio de nombres. En comparación, Vuex 5 parece funcionar exactamente como esperarías normalmente:
store.count // Access State store.double // Access Getters (transparent) store.increment() // Run actions // No Mutators
Todo, el estado, los captadores y las acciones, está disponible directamente en la raíz de la tienda, lo que facilita su uso con mucha menos verbosidad y prácticamente elimina toda necesidad de usar mapState
, mapGetters
, mapActions
y mapMutations
para la API de opciones o para escribir. declaraciones computed
adicionales o funciones simples para la API de composición. Esto simplemente hace que una tienda Vuex se vea y actúe como una tienda normal que usted mismo construiría, pero obtiene todos los beneficios de los complementos, las herramientas de depuración, la documentación oficial, etc.
Tiendas de composición
El aspecto final de Vuex 5 que veremos hoy es la componibilidad. Vuex 5 no tiene módulos con espacio de nombres a los que se pueda acceder desde una única tienda. Cada uno de esos módulos se dividiría en una tienda completamente separada. Eso es bastante simple de manejar para los componentes: simplemente importan las tiendas que necesitan, las encienden y las usan. Pero, ¿qué pasa si una tienda quiere interactuar con otra tienda? En v4, el espacio de nombres lo complica todo, por lo que debe usar el espacio de nombres en sus llamadas de commit
y dispatch
, usar rootGetters
y rootState
y luego avanzar hacia los espacios de nombres desde los que desea acceder a los captadores y al estado. Así es como funciona en Vuex 5:
// store/greeter.js import { defineStore } from 'vuex' export default defineStore({ name: 'greeter', state () { return { greeting: 'Hello' } } }) // store/counter.js import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore({ name: 'counter', // Then `use` the store use () { return { greeter: greeterStore } }, state () { return { count: 0 } }, getters: { greetingCount () { return `${this.greeter.greeting} ${this.count}' // access it from this.greeter } } })
Con v5, importamos la tienda que deseamos usar, luego la registramos con use
y ahora es accesible en toda la tienda en cualquier nombre de propiedad que le haya dado. Las cosas son aún más simples si está utilizando la variación API de composición de la definición de la tienda:
// store/counter.js import { ref, computed } from 'vue' import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore('counter', ({use}) => { // `use` is passed in to function const greeter = use(greeterStore) // use `use` and now you have full access const count = 0 const greetingCount = computed(() => { return `${greeter.greeting} ${this.count}` // access it like any other variable }) return { count, greetingCount } })
No más módulos con espacio de nombres. Cada tienda es independiente y se utiliza por separado. Puedes usar use
para hacer que una tienda esté disponible dentro de otra tienda para componerlos. En ambos ejemplos, el use
es básicamente el mismo mecanismo que vuex.store
anterior y aseguran que instanciamos las tiendas con la instancia correcta de Vuex.
Compatibilidad con mecanografiado
Para los usuarios de TypeScript, uno de los mejores aspectos de Vuex 5 es que la simplificación hizo más sencillo agregar tipos a todo. Las capas de abstracción que tenían las versiones anteriores de Vuex lo hacían casi imposible y en este momento, con Vuex 4, aumentaron nuestra capacidad de usar tipos, pero todavía hay demasiado trabajo manual para obtener una cantidad decente de soporte de tipos, mientras que en v5 , puede poner sus tipos en línea, tal como espera y espera.
Conclusión
Vuex 5 parece ser casi exactamente lo que yo, y probablemente muchos otros, esperaban que fuera, y siento que no llegará lo suficientemente pronto. Simplifica la mayor parte de Vuex, elimina parte de la sobrecarga mental involucrada, y solo se vuelve más complicado o detallado donde agrega flexibilidad. Deje comentarios a continuación sobre lo que piensa de estos cambios y qué cambios podría hacer en su lugar o además. O vaya directamente a la fuente y agregue un RFC (Solicitud de comentarios) a la lista para ver qué piensa el equipo central.