驴Cu谩l ser谩 la nueva versi贸n de Vuex?

Vuex es un administrador estatal de aplicaciones Vue. Su pr贸xima versi贸n es Vuex 4, que est谩 casi lista para su lanzamiento oficial. Agregar谩 soporte para Vue 3, pero no traer谩 ninguna funcionalidad nueva.



Si bien Vuex se considera una gran soluci贸n y muchos desarrolladores la eligen como su biblioteca de administraci贸n de estado principal, esperan obtener m谩s funciones en versiones futuras. Por lo tanto, mientras Vuex 4 se est谩 preparando para su lanzamiento, uno de sus desarrolladores, Kia King Ishii (parte del equipo central) ya est谩 compartiendo planes para la pr贸xima versi贸n 5. Vale la pena se帽alar que estos son solo planes y algunas cosas pueden cambiar, sin embargo, la direcci贸n principal ya se ha elegido. Sobre 茅l se discutir谩.



Con la llegada de Vue 3 y la API de composici贸n , los desarrolladores comenzaron a crear alternativas simples. Por ejemplo, el art铆culo " Probablemente no necesite Vuex " demuestra una forma simple, flexible y confiable de crear tiendas basadas en la API de composici贸n junto con provide/inject



. Podemos suponer que esta y algunas otras alternativas est谩n bien para aplicaciones peque帽as, pero como suele suceder, tienen sus inconvenientes: documentaci贸n, comunidad, convenciones de nomenclatura, integraci贸n, herramientas de desarrollo.







El 煤ltimo punto es muy importante. Vue ahora tiene una gran extensi贸n de navegadorpara ayudar con el desarrollo y la depuraci贸n. Eliminarlo puede ser un gran desperdicio, especialmente cuando se construyen grandes aplicaciones. Afortunadamente, esto no suceder谩 con Vuex 5. En cuanto a los enfoques alternativos, funcionar谩n, pero no traer谩n tantos beneficios como la soluci贸n oficial. Por tanto, veamos qu茅 tipo de ventajas nos prometen.



Creando una tienda



Antes de hacer algo con el lateral, debemos crearlo. En Vuex 4, se ve 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')
    }
  }
})

      
      





La tienda tambi茅n consta de 4 partes: estado, donde se almacenan los datos; captadores que proporcionan estados calculados; mutaciones necesarias para cambiar de estado y acciones que se llaman fuera de la tienda para realizar operaciones en ella. Por lo general, las acciones no solo causan mutaci贸n (como en el ejemplo), sino que se utilizan para realizar tareas asincr贸nicas (porque las mutaciones deben ser sincr贸nicas ) o implementar alguna l贸gica m谩s compleja. 驴C贸mo se ver谩 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++
    }
  }
})

      
      





Lo primero que ha cambiado es el cambio createStore



de nombre a defineStore



. Un poco m谩s tarde quedar谩 claro por qu茅. A continuaci贸n, hab铆a un par谩metro name



para especificar el nombre de la tienda. Antes de eso, dividimos los lados en m贸dulos, y los nombres de los m贸dulos ten铆an la forma de objetos con nombre. Adem谩s, los m贸dulos se registraron en el espacio global, por lo que no eran autosuficientes y no estaban listos para su reutilizaci贸n. Como soluci贸n, se ten铆a que utilizar un par谩metro namespaced



para evitar que varios m贸dulos respondieran al mismo tipo de mutaciones y acciones. Creo que muchos se han encontrado con esto, pero de todos modos agregar茅 un enlace a la documentaci贸n . Ahora no tenemos m贸dulos, cada tienda es por defecto un almacenamiento separado e independiente.



Despu茅s de especificar el nombre, debemos convertirlo en una state



funci贸n que devuelva el estado inicial, no solo lo establezca. Esto es muy similar a lo que parece data



en los componentes. Los cambios tambi茅n afectaron a los captadores, en lugar de state



utilizar una funci贸n como par谩metro this



para acceder a los datos. El mismo enfoque se aplica a las acciones, en this



lugar de stat



como par谩metro. Finalmente, y lo m谩s importante, las mutaciones se combinan con juegos de acci贸n. Kia celebr贸que las mutaciones a menudo se convierten en simples establecedores, haci茅ndolos verbosos, aparentemente esta fue la raz贸n de la eliminaci贸n. No menciona si ser谩 posible realizar cambios de estado fuera de la tienda, por ejemplo, de componentes. Aqu铆, solo podemos referirnos al patr贸n Flux, que no recomienda hacer esto y fomenta un enfoque de cambio de estado de las acciones.



Anexo: Quienes utilicen la API de composici贸n para crear componentes estar谩n encantados de saber que existe una forma de crear una tienda de manera similar.



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 }  
})

      
      





En el ejemplo anterior, pasamos el nombre de la tienda como primer argumento defineStore



. El resto es la API de composici贸n habitual, y el resultado ser谩 exactamente el mismo que en el ejemplo con la API cl谩sica.



Inicializaci贸n de la tienda



Aqu铆 nos esperan cambios importantes. Para describir c贸mo se llevar谩 a cabo la inicializaci贸n de la tienda en la quinta versi贸n, veamos c贸mo ocurre en la cuarta. Cuando creamos una tienda a trav茅s de createStore



, la inicializamos inmediatamente, para que luego podamos usarla en app.use



o directamente.



import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

const app = createApp(App)

app.use(store)
app.mount('#app')

//        `this.$store`
//   `useStore()`   Composition API

import store from './store'

store.state.count // -> 0
store.commit('increment')
store.dispatch('increment')
store.getters.double // -> 4

      
      





En la versi贸n 5, accedemos por separado a cada instancia de Vuex, lo que garantiza la independencia. Por lo tanto, este proceso se ve diferente:



import { createApp } from 'vue'
import { createVuex } from 'vuex'
import App from './App.vue'

const app = createApp(App)
const vuex = createVuex()

app.use(vuex)
app.mount('#app')

      
      





Todos los componentes ahora tienen la capacidad de acceder a cualquier instancia de Vuex directamente, en lugar de acceder al espacio global. Mira un ejemplo:



import { defineComponent } from 'vue'
import store from './store'

export default defineComponent({
  name: 'App',

  computed: {
    counter () {
      return this.$vuex.store(store)
    }
  }
})

      
      





La llamada $vuex.store



crea e inicializa la tienda (recuerde cambiar el nombre createStore



). Ahora, cada vez que se comunique con este repositorio $vuex.store



, se le devolver谩 una instancia ya creada. En el ejemplo, esto this.counter



es lo que podemos usar m谩s en el c贸digo. Tambi茅n puede inicializar la tienda a trav茅s de createVuex()



.



Y, por supuesto, una opci贸n para la API de composici贸n, donde se $vuex.store



usa en su lugar useStore



.



import { defineComponent } from 'vue'
import { useStore } from 'vuex'
import store from './store'

export default defineComponent({
  setup () {
    const counter = useStore(store)

    return { counter }
  }
})

      
      





El enfoque descrito anteriormente (inicializar la tienda a trav茅s de componentes) tiene ventajas y desventajas. Por un lado, se trata de la separaci贸n del c贸digo y la posibilidad de agregarlo solo cuando sea necesario. Por otro lado, agregar una dependencia (ahora debe importar la tienda cada vez que planee usarla). Por lo tanto, si desea usar DI, entonces una opci贸n que usa 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('name', store)
app.mount('#app')

      
      





Y luego lanzando la tienda al componente (existe el deseo de reemplazarlo name



con una constante y ya usarlo):



import { defineComponent } from 'vue'

export default defineComponent({
  name: 'App',
  inject: ['name']
})

// Composition API

import { defineComponent, inject } from 'vue'

export default defineComponent({
  setup () {
    const store = inject('name')

    return { store }
  }
})

      
      





No hay mucho entusiasmo por esta soluci贸n, pero parece m谩s expl铆cita y flexible que el enfoque actual. No se puede decir lo mismo sobre el uso posterior. Ahora se ve as铆:



store.state.count            // State
store.getters.double         // Getters
store.commit('increment')    // Mutations
store.dispatch('increment')  // Actions

      
      





Se espera que la nueva versi贸n:



store.count        // State
store.double       // Getters
store.increment()  // Actions

      
      





Todas las entidades (estado, captadores y acciones) son directamente accesibles, lo que hace que trabajar con ellas sea m谩s f谩cil y l贸gico. Al mismo tiempo que se elimina la necesidad de mapState



, mapGetters



, mapActions



y mapMutations



, adem谩s de escribir calculados adicionales propiedades.



Compartiendo



El 煤ltimo punto a considerar es compartir. Recordamos que en Vuex 5 ya no tenemos m贸dulos con nombre y cada lado es separado e independiente. Esto hace posible importarlos cuando sea necesario y usar los datos seg煤n sea necesario, al igual que los componentes. Surge una pregunta l贸gica, 驴c贸mo usar varias tiendas juntas? En la versi贸n 4 todav铆a hay un espacio de nombres global y necesitamos usar rootGetters



y rootState



hacer referencia a diferentes tiendas en esta 谩rea (como en la versi贸n 3). El enfoque en Vuex 5 es diferente:



// 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'

export default defineStore({
  name: 'counter',

  use () {
    return { greeter: greeterStore }
  },
  
  state () {
    return { count: 0 }
  },
  
  getters: {
    greetingCount () {
      return `${this.greeter.greeting} ${this.count}'
    }
  }
})
      
      





Importamos la tienda, luego la registramos use



y accedemos a ella. Todo parece a煤n m谩s f谩cil si usa la API de composici贸n:



// store/counter.js
import { ref, computed } from 'vue'
import { defineStore } from 'vuex'
import greeterStore from './greeter'

export default defineStore('counter', ({use}) => {
  const greeter = use(greeterStore)
  const count = 0

  const greetingCount = computed(() => {
    return  `${greeter.greeting} ${this.count}`
  })

  return { count, greetingCount }
})

      
      





Lo 煤nico que vale la pena mencionar es que use



funciona exactamente de la misma manera vuex.store



y se encarga de la correcta inicializaci贸n de las tiendas.



Soporte TypeScript



Con cambios en la API y menos abstracciones, la compatibilidad con TypeScript en la versi贸n 4 ser谩 mucho mejor, pero a煤n necesitamos mucho trabajo manual. El lanzamiento de la versi贸n 5 permitir谩 agregar tipos donde sea necesario y donde queramos.



Conclusi贸n



Vuex 5 parece prometedor y es exactamente lo que muchos esperan (corregir errores antiguos, agregar flexibilidad). Se puede encontrar una lista completa de las discusiones y opiniones del equipo central en el repositorio de RFC de Vue .



All Articles