Správa stavu
Co je správa stavu?
Technicky vzato každá instance Vue komponenty již svůj vlastní reaktivní stav „spravuje“. Jako příklad si vezměme jednoduchou komponentu počítadla:
vue
<script setup> import { ref } from 'vue' // stav const count = ref(0) // akce function increment() { count.value++ } </script> <!-- zobrazení --> <template>{{ count }}</template>Je to samostatná jednotka s následujícími částmi:
- Stav – zdroj pravdy, který naši aplikaci řídí;
- Zobrazení – deklarativní mapování stavu;
- Akce – možné způsoby, jak se stav může změnit v reakci na uživatelské vstupy ze zobrazení.
Toto je jednoduché zobrazení konceptu „jednosměrného toku dat“:

Bohužel, jednoduchost začíná selhávat, když máme více komponent, které sdílejí společný stav:
- Více zobrazení může záviset na stejné části stavu.
- Akce z různých zobrazení mohou potřebovat měnit stejnou část stavu.
Pro případ jedna je možným řešením „“vyzdvihnutí“ sdíleného stavu na společného předka komponenty a poté ho předat dolů jako vlastnost (props). Nicméně ve stromech komponent s hlubokou hierarchií to rychle začíná být zdlouhavé a vede to k dalšímu problému známému jako drilling vlastností.
Pro případ dva se často uchylujeme k řešením, jako je přístup k přímým instancím komponent rodiče / potomka pomocí template refs nebo pokusy o změnu a synchronizaci více kopií stavu pomocí emitovaných událostí (emits). Obě tyto vzorce jsou křehké a rychle vedou k neudržovatelnému kódu.
Jednodušším a přímočařejším řešením je vyjmout sdílený stav z komponent a spravovat ho v globálním singleton objektu. Díky tomu se náš strom komponent stává velkým „pohledem“ (view) a jakákoli komponenta může přistupovat ke stavu nebo spouštět akce, bez ohledu na to, kde ve stromu se nacházejí!
Jednoduchá správa stavu s Reactivity API
Pokud máte část stavu, která by měla být sdílena mezi více instancemi, můžete k vytvoření reaktivního objektu použít reactive(), a poté jej importovat do více komponent:
js
import { reactive } from 'vue' export const store = reactive({ count: 0 })vue
<script setup> import { store } from './store.js' </script> <template>Od A: {{ store.count }}</template>vue
<script setup> import { store } from './store.js' </script> <template>Od B: {{ store.count }}</template>Kdykoli je nyní objekt store změněn, obě komponenty <ComponentA> a <ComponentB> automaticky aktualizují svá zobrazení – máme jediný zdroj pravdy.
Ovšem to také znamená, že jakákoli komponenta, která store importuje, ho může libovolně měnit:
template
<template> <button @click="store.count++"> Od B: {{ store.count }} </button> </template>Přestože to funguje v jednoduchých případech, globální stav, který může být libovolně měněn kteroukoli komponentou, nebude dlouhodobě udržitelný. Aby byla logika změny stavu centralizována stejně jako samotný stav, doporučuje se definovat v úložišti stavu metody s názvy, které vyjadřují úmysl akcí:
js
// store.js import { reactive } from 'vue' export const store = reactive({ count: 0, increment() { this.count++ } })template
<template> <button @click="store.increment()"> Od B: {{ store.count }} </button> </template>TIP
Všimněte si, že click handler používá store.increment() s kulatými závorkami. Je to nutné pro volání metody s odpovídajícím kontextem this, protože to není metoda komponenty.
I když zde jako úložiště používáme jediný reaktivní objekt, můžete také sdílet reaktivní stav vytvořený pomocí jiných funkcí Reactivity API jako ref() nebo computed(), nebo dokonce vrátit globální stav z composable:
js
import { ref } from 'vue' // globální stav, vytvořený v rámci modulu const globalCount = ref(1) export function useCount() { // lokální stav, vytvořený pro každou komponentu const localCount = ref(1) return { globalCount, localCount } }Skutečnost, že je reaktivní systém Vue od modelu komponent oddělený, ho činí extrémně flexibilním.
Úvahy o SSR
Pokud vytváříte aplikaci, která využívá vykreslování na serveru (SSR), výše uvedený vzor může vést k problémům kvůli sdílenému úložišti, které je singletonem sdíleným v rámci více požadavků. Tento problém je podrobněji popsán v průvodci SSR.
Pinia
I když naše vlastní řešení pro správu stavu v jednoduchých scénářích postačuje, existuje mnoho dalších věcí, které je třeba při vývoji rozsáhlých produkčních aplikací zvážit:
- Silnější konvence pro týmovou spolupráci
- Integrace s Vue DevTools, včetně časové osy, inspekce komponent a ladění s možností cestování časem
- Podpora Hot Module Replacement (HMR)
- Podpora Server-Side Rendering (SSR)
Pinia je knihovna pro správu stavu, která implementuje všechny výše uvedené funkce. Je udržována Vue týmem a funguje jak s Vue 2, tak s Vue 3.
Existující uživatelé mohou mít zkušenosti s Vuex, předchozí oficiální knihovnou pro správu stavu ve Vue. S Pinia, která plní v ekosystému stejnou roli, je nyní Vuex pouze v režimu údržby. Stále funguje, ale již nezískává nové funkce. Pro nové aplikace se doporučuje používat Pinia.
Pinia začala jako průzkum toho, jak by mohla další iterace Vuex vypadat, a zahrnuje mnoho nápadů z diskusí Vue týmu pro Vuex 5. Nakonec jsme si uvědomili, že Pinia již implementuje většinu toho, co jsme chtěli ve Vuex 5, a rozhodli jsme se ji doporučit jako nové řešení.
Ve srovnání s Vuex poskytuje Pinia jednodušší API s méně obřadným zápisem, nabízí API ve stylu Composition API a především má solidní podporu pro odvozování typů při použití s TypeScriptem.