Ce urmează la VueX?
Publicat: 2022-03-10Vuex este soluția pentru managementul statului în aplicațiile Vue. Următoarea versiune — Vuex 4 — își face drum prin pașii finali înainte de lansarea oficială. Această versiune va aduce compatibilitate deplină cu Vue 3, dar nu adaugă funcții noi. Deși Vuex a fost întotdeauna o soluție puternică și prima alegere pentru mulți dezvoltatori pentru managementul de stat în Vue, unii dezvoltatori au sperat să vadă mai multe probleme de flux de lucru abordate. Cu toate acestea, chiar dacă Vuex 4 tocmai iese pe ușă, Kia King Ishii (un membru al echipei de bază Vue) vorbește despre planurile sale pentru Vuex 5 și sunt atât de încântat de ceea ce am văzut încât a trebuit să vă împărtășesc toate. Rețineți că planurile Vuex 5 nu sunt finalizate, așa că unele lucruri se pot schimba înainte de lansarea Vuex 5, dar dacă se termină în mare parte similar cu ceea ce vedeți în acest articol, ar trebui să fie o mare îmbunătățire a experienței dezvoltatorului.
Odată cu apariția Vue 3 și a API-ului său de compoziție, oamenii au căutat alternative simple construite manual. De exemplu, S-ar putea să nu aveți nevoie de Vuex demonstrează un model relativ simplu, dar flexibil și robust pentru utilizarea API-ului de compoziție împreună cu provide/inject
pentru a crea magazine de stat partajate. După cum afirmă Gabor în articolul său, totuși, aceasta (și alte alternative) ar trebui să fie utilizate numai în aplicații mai mici, deoarece le lipsesc toate acele lucruri care nu sunt direct legate de cod: suport comunitar, documentație, convenții, integrări bune Nuxt și dezvoltatori. unelte.
Ultima a fost întotdeauna una dintre cele mai mari probleme pentru mine. Extensia de browser Vue devtools a fost întotdeauna un instrument uimitor pentru depanarea și dezvoltarea aplicațiilor Vue, iar pierderea inspectorului Vuex cu „călătoria în timp” ar fi o pierdere destul de mare pentru depanarea oricăror aplicații non-triviale.

Din fericire, cu Vuex 5 vom putea să ne luăm prăjitura și să-l mâncăm și noi. Va funcționa mai mult ca aceste alternative API de compoziție, dar va păstra toate beneficiile utilizării unei biblioteci oficiale de management de stat. Acum să aruncăm o privire la ceea ce se va schimba.
Definirea unui magazin
Înainte de a putea face ceva cu un magazin Vuex, trebuie să definim unul. În Vuex 4, o definiție de magazin va arăta astfel:
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') } } })
Fiecare magazin are patru părți: state
stochează datele, getters
vă oferă starea calculată, mutations
sunt folosite pentru a modifica starea, iar actions
sunt metodele care sunt apelate din afara magazinului pentru a realiza orice are legătură cu magazinul. De obicei, acțiunile nu comit doar o mutație, așa cum arată acest exemplu. În schimb, sunt folosiți pentru a face sarcini asincrone, deoarece mutațiile trebuie să fie sincrone sau doar implementează funcționalități mai complicate sau cu mai mulți pași. De asemenea, acțiunile nu pot muta statul pe cont propriu; trebuie să folosească un mutator. Deci cum arată 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++ } } })
Sunt câteva modificări de remarcat aici. În primul rând, în loc de createStore
, folosim defineStore
. Această diferență este neglijabilă, dar există din motive semantice, pe care le vom analiza mai târziu. Apoi, trebuie să oferim un name
pentru magazin, de care nu aveam nevoie înainte. În trecut, modulele aveau propriul nume, dar nu erau furnizate de modulul în sine; erau doar numele proprietății cărora le-au fost atribuite de magazinul părinte care le-a adăugat. Acum, nu există module . În schimb, fiecare modul va fi un magazin separat și va avea un nume. Acest nume este folosit de registrul Vuex, despre care vom vorbi mai târziu.
După aceea, trebuie să facem state
o funcție care returnează starea inițială în loc să o setăm doar la starea inițială. Aceasta este similară cu opțiunea de data
pentru componente. Scriem getters
foarte asemănător cu modul în care am făcut-o în Vuex 4, dar în loc să folosim state
ca parametru pentru fiecare getter, puteți doar să utilizați this
pentru a ajunge la stare. În același mod, actions
nu trebuie să-și facă griji cu privire la transmiterea unui obiect context
: ele pot folosi doar this
pentru a accesa totul. În cele din urmă, nu există mutations
. În schimb, mutațiile sunt combinate cu actions
. Kia a remarcat că de prea multe ori, mutațiile au devenit simpli setatori, făcându-le inutil de verbose, așa că le-au eliminat. El nu a menționat dacă este „ok” să modificăm starea direct din afara magazinului, dar cu siguranță avem voie și încurajați să modificăm starea direct dintr-o acțiune, iar modelul Flux se încruntă asupra mutației directe a stării.
Notă : pentru cei care preferă API-ul de compoziție față de API-ul de opțiuni pentru crearea de componente, veți fi bucuroși să aflați că există și o modalitate de a crea magazine într-un mod similar cu utilizarea API-ului de compoziție.
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 } })
După cum se arată mai sus, numele este transmis ca prim argument pentru defineStore
. Restul arată exact ca o funcție de compoziție pentru componente. Acest lucru va produce exact același rezultat ca exemplul anterior care a folosit opțiunile API.
Instanțierea magazinului
În Vuex 4, lucrurile s-au schimbat față de Vuex 3, dar mă voi uita doar la v4 pentru a împiedica lucrurile să scape de sub control. În v4, când ați apelat createStore
, l-ați instanțiat deja. Apoi îl puteți utiliza în aplicația dvs., fie prin app.use
, fie direct:
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
Acesta este un lucru pe care Vuex 5 îl face puțin mai complicat decât în v4. Fiecare aplicație poate obține acum o instanță separată de Vuex, ceea ce se asigură că fiecare aplicație poate avea instanțe separate ale acelorași magazine fără a partaja date între ele. Puteți partaja o instanță de Vuex dacă doriți să partajați instanțe de magazine între aplicații.

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')
Acum toate componentele dvs. au acces la instanța Vuex. În loc să oferiți direct definiția magazinului (magazinelor), le importați în componentele în care doriți să le utilizați și utilizați instanța Vuex pentru a le instanția și a le înregistra:
import { defineComponent } from 'vue' import store from './store' export default defineComponent({ name: 'App', computed: { counter () { return this.$vuex.store(store) } } })
Apelând $vuex.store
, instanțiază și înregistrează magazinul în instanța Vuex. Din acel moment, de fiecare dată când utilizați $vuex.store
pe acel magazin, vă va returna magazinul deja instanțiat în loc să îl instanțiați din nou. Puteți apela metoda store
direct pe o instanță Vuex creată de createVuex()
.
Acum magazinul dvs. este accesibil pe acea componentă prin this.counter
. Dacă utilizați API-ul de compoziție pentru componenta dvs., puteți utiliza useStore
în loc 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 } } })
Există argumente pro și contra în importarea magazinului direct în componentă și instanțierea lui acolo. Vă permite să divizați codul și să încarce leneș magazinul doar acolo unde este nevoie, dar acum este o dependență directă în loc să fie injectată de un părinte (să nu mai vorbim că trebuie să-l importați de fiecare dată când doriți să-l utilizați). Dacă doriți să utilizați injecția de dependență pentru a o furniza în întreaga aplicație, mai ales dacă știți că va fi folosită la rădăcina aplicației, unde divizarea codului nu va ajuta, atunci puteți utiliza doar 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')
Și îl puteți injecta în orice componentă în care o veți folosi:
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 } } })
Nu sunt entuziasmat de această verbozitate suplimentară, dar este mai explicită și mai flexibilă, ceea ce sunt un fan. Acest tip de cod este în general scris o dată imediat la începutul proiectului și apoi nu vă deranjează din nou, deși acum va trebui fie să furnizați fiecare magazin nou, fie să îl importați de fiecare dată când doriți să îl utilizați, dar importarea sau injectarea modulelor de cod este modul în care, în general, trebuie să lucrăm cu orice altceva, deci doar face ca Vuex să funcționeze mai mult în conformitate cu modul în care oamenii tind să lucreze deja.
Utilizarea unui magazin
Pe lângă faptul că sunt un fan al flexibilității și al noului mod de a defini magazinele în același mod ca o componentă care utilizează API-ul de compoziție, mai există un lucru care mă face mai entuziasmat decât orice altceva: cum sunt folosite magazinele. Iată cum arată să folosești un magazin în Vuex 4.
store.state.count // Access State store.getters.double // Access Getters store.commit('increment') // Mutate State store.dispatch('increment') // Run Actions
State
, getters
, mutations
și actions
sunt toate gestionate în moduri diferite prin diferite proprietăți sau metode. Acest lucru are avantajul explicității, pe care l-am lăudat mai devreme, dar această claritate nu ne câștigă cu adevărat nimic. Și acest API devine mai dificil de utilizat doar atunci când utilizați module spațiate de nume. Prin comparație, Vuex 5 pare să funcționeze exact așa cum ați spera în mod normal:
store.count // Access State store.double // Access Getters (transparent) store.increment() // Run actions // No Mutators
Totul - starea, getters și acțiuni - este disponibil direct la rădăcina magazinului, făcându-l simplu de utilizat cu mult mai puțină verbozitate și practic elimină orice nevoie de utilizare mapState
, mapGetters
, mapActions
și mapMutations
pentru opțiunile API sau pentru scriere. instrucțiuni extra computed
sau funcții simple pentru API-ul de compoziție. Acest lucru pur și simplu face ca un magazin Vuex să arate și să acționeze exact ca un magazin normal pe care l-ați construi singur, dar beneficiază de toate beneficiile pluginurilor, instrumentelor de depanare, documentației oficiale etc.
Magazine de compunere
Aspectul final al Vuex 5 pe care îl vom analiza astăzi este compozibilitatea. Vuex 5 nu are module spațiate de nume care sunt toate accesibile din magazinul unic. Fiecare dintre aceste module ar fi împărțit într-un magazin complet separat. Este destul de simplu de rezolvat pentru componente: ei doar importă magazinele de care au nevoie și le pornesc și le folosesc. Dar dacă un magazin dorește să interacționeze cu un alt magazin? În v4, spația de nume încurcă totul, așa că trebuie să utilizați spațiul de nume în apelurile dvs. de commit
și de dispatch
, să utilizați rootGetters
și rootState
și apoi să vă urcați în spațiile de nume din care doriți să accesați getters și state. Iată cum funcționează în 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 } } })
Cu v5, importăm magazinul pe care dorim să-l folosim, apoi îl înregistrăm cu use
și acum este accesibil în tot magazinul, indiferent de numele proprietății i-ați dat. Lucrurile sunt și mai simple dacă utilizați varianta API-ului de compoziție a definiției magazinului:
// 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 } })
Gata cu modulele cu spații de nume. Fiecare magazin este separat și este folosit separat. Puteți folosi use
pentru a face un magazin disponibil în alt magazin pentru a le compune. În ambele exemple, use
este practic același mecanism ca și vuex.store
de mai devreme și se asigură că instanțiam magazinele cu instanța corectă a Vuex.
Suport TypeScript
Pentru utilizatorii TypeScript, unul dintre cele mai mari aspecte ale Vuex 5 este că simplificarea a făcut mai simplă adăugarea de tipuri la orice. Straturile de abstractizare pe care versiunile mai vechi ale Vuex le-au făcut aproape imposibilă și acum, cu Vuex 4, ne-au crescut capacitatea de a folosi tipuri, dar există încă prea multă muncă manuală pentru a obține o cantitate decentă de suport de tip, în timp ce în v5. , vă puteți pune tipurile în linie, așa cum v-ați aștepta și v-ați aștepta.
Concluzie
Vuex 5 pare să fie aproape exact ceea ce eu – și probabil mulți alții – am sperat că va fi și simt că nu poate veni destul de curând. Simplifică cea mai mare parte a Vuex, eliminând o parte din suprasarcina mentală implicată și devine mai complicată sau mai pronunțată doar acolo unde adaugă flexibilitate. Lăsați comentarii mai jos despre ce părere aveți despre aceste modificări și ce modificări ați putea face în loc sau în plus. Sau mergeți direct la sursă și adăugați un RFC (Solicitare de comentarii) la listă pentru a vedea ce părere are echipa de bază.