Skip to content

Commit

Permalink
feat: polishing - composables
Browse files Browse the repository at this point in the history
  • Loading branch information
AloisSeckar committed Feb 17, 2024
1 parent 396a183 commit 98455e1
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 20 deletions.
38 changes: 19 additions & 19 deletions src/guide/reusability/composables.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ const { x, y } = useMouse()
</script>

:::tip
Tato část předpokládá základní znalost Composition API. Pokud jste se učili Vue pouze s Options API, můžete nastavit předvolbu API na Composition API (pomocí přepínače v horní části levého postranního panelu) a znovu si přečíst kapitoly [Základy reaktivity](/guide/essentials/reactivity-fundamentals) a [Lifecycle Hooks](/guide/essentials/lifecycle).
Tato část předpokládá základní znalost Composition API. Pokud jste se učili Vue pouze s&nbsp;Options API, můžete nastavit předvolbu API na Composition API (pomocí přepínače v&nbsp;horní části levého postranního panelu) a znovu si přečíst kapitoly [Základy reaktivity](/guide/essentials/reactivity-fundamentals) a&nbsp;[Lifecycle Hooks](/guide/essentials/lifecycle).
:::

## Co je to "Composable"? {#what-is-a-composable}

V kontextu Vue aplikací je "composable" funkce, která maximálně využívá Vue Composition API k obalení a znovupoužití **stavové (stateful) logiky**.
V kontextu Vue aplikací je composable funkce, která maximálně využívá Vue Composition API k obalení a znovupoužití **stavové (stateful) logiky**.

Při vytváření frontendových aplikací často potřebujeme znovupoužívat logiku pro běžné úkoly. Například můžeme potřebovat formátovat data na mnoha místech, takže pro to vytvoříme znovupoužitelnou funkci. Tato formátovací funkce zapouzdřuje **bezstavovou (stateless) logiku**: přijme nějaký vstup a okamžitě vrátí očekávaný výstup. Pro znovupoužití bezstavové logiky existuje mnoho knihoven - například [lodash](https://lodash.com/) a [date-fns](https://date-fns.org/), o kterých jste možná už slyšeli.

Naproti tomu stavová logika zahrnuje management stavu (state), který se v průběhu času mění. Jednoduchým příkladem může být sledování aktuální polohy myši na stránce. V reálných aplikacích by se mohlo jednat i o složitější logiku, jako jsou dotyková gesta nebo stav připojení k databázi.

## Příklad sledování polohy myši {#mouse-tracker-example}

Pokud bychom implementovali funkci sledování myši pomocí Composition API přímo v komponentě, vypadalo by to takto:
Pokud bychom implementovali funkci sledování myši pomocí Composition API přímo v&nbsp;komponentě, vypadalo by to takto:

```vue
<script setup>
Expand Down Expand Up @@ -89,7 +89,7 @@ const { x, y } = useMouse()

Jak vidíme, jádro logiky zůstává stejné - stačilo ji přesunout do externí funkce a vrátit stav, který má být vystaven. Stejně jako uvnitř komponenty můžete v composable objektech používat kompletní paletu funkcí [Composition API](/api/#composition-api). Stejnou funkci `useMouse()` lze nyní použít v jakékoli komponentě.

Ještě větší výhodou composables je, že je můžete také vnořovat: jedna composables funkce může volat jednu nebo více dalších composables. To nám umožňuje skládat složitou logiku pomocí malých, izolovaných jednotek, podobně jako skládáme celou aplikaci pomocí komponent. To je vlastně důvod, proč jsme se rozhodli nazvat kolekci rozhraní API, která tento vzor umožňuje, Composition API.
Ještě větší výhodou composables je, že je můžete také vnořovat: jedna composable funkce může volat jednu nebo více dalších composables. To nám umožňuje skládat složitou logiku pomocí malých, izolovaných jednotek, podobně jako skládáme celou aplikaci pomocí komponent. To je vlastně důvod, proč jsme se rozhodli nazvat kolekci rozhraní API, která tento vzor umožňuje, Composition API.

Například logiku přidávání a odebírání DOM event listeneru můžeme vyčlenit do vlastní composable funkce:

Expand Down Expand Up @@ -126,7 +126,7 @@ export function useMouse() {
```

:::tip
Každá instance komponenty volající `useMouse()` vytvoří své vlastní kopie stavu `x` a `y`, takže se nebudou vzájemně rušit. Pokud chcete spravovat sdílený stav mezi komponentami, přečtěte si kapitolu [State management](/guide/scaling-up/state-management).
Každá instance komponenty volající `useMouse()` vytvoří své vlastní kopie stavu `x` a `y`, takže se nebudou vzájemně rušit. Pokud chcete spravovat sdílený stav mezi komponentami, přečtěte si kapitolu [Správa stavu](/guide/scaling-up/state-management).
:::

## Příklad s asynchronním stavem {#async-state-example}
Expand Down Expand Up @@ -187,7 +187,7 @@ const { data, error } = useFetch('...')

### Přijímání reaktivního stavu {#accepting-reactive-state}

`useFetch()` přijímá jako vstup statický URL string - načtení tedy provede pouze jednou a poté je hotovo. Co když chceme, aby se načítání opakovalo, kdykoli se změní adresa URL? Toho můžeme dosáhnout tím, že do composable funkce předáme reaktivní stav a necháme composable vytvořit watchery, které budou provádět akce s použitím předaného stavu.
`useFetch()` přijímá jako vstup statický URL string - načtení tedy provede pouze jednou a poté je hotovo. Co když chceme, aby se načítání opakovalo, kdykoli se změní adresa URL? Toho můžeme dosáhnout tím, že do composable funkce předáme reaktivní stav a&nbsp;necháme composable vytvořit watchery, které budou provádět akce s použitím předaného stavu.

Například `useFetch()` by mělo být schopno akceptovat ref:

Expand All @@ -203,11 +203,11 @@ url.value = '/new-url'
Nebo akceptovat getter funkci:

```js
// re-fetch when props.id changes
// re-fetch když se změní props.id
const { data, error } = useFetch(() => `/posts/${props.id}`)
```

Můžeme refaktorovat naši existující implemntaci pomocí API funkcí [`watchEffect()`](/api/reactivity-core.html#watcheffect) a [`toValue()`](/api/reactivity-utilities.html#tovalue):
Můžeme refaktorovat naši existující implemntaci pomocí API funkcí [`watchEffect()`](/api/reactivity-core.html#watcheffect) a&nbsp;[`toValue()`](/api/reactivity-utilities.html#tovalue):

```js{8,13}
// fetch.js
Expand Down Expand Up @@ -236,11 +236,11 @@ export function useFetch(url) {
}
```

`toValue()` je API přidané ve Vue 3.3. Je určeno k normalizaci refs a getter funkcí na hodnoty. Pokud je vstupním parametrem ref, vrátí jeho .value, pokud je parametrem funkce, zavolá funkci a vrátí její návratovou hodnotu. Jinak vrátí parametr tak, jak byl předán. Funguje podobně jako [`unref()`](/api/reactivity-utilities.html#unref), ale se speciálním přístupem k funkcím.
`toValue()` je API přidané ve Vue 3.3. Je určeno k normalizaci refs a getter funkcí na hodnoty. Pokud je vstupním parametrem ref, vrátí jeho `.value`, pokud je parametrem funkce, zavolá funkci a vrátí její návratovou hodnotu. Jinak vrátí parametr tak, jak byl předán. Funguje podobně jako [`unref()`](/api/reactivity-utilities.html#unref), ale se speciálním přístupem k funkcím.

Všimněte si, že `toValue(url)` je voláno **uvnitř** callbacku funkce `watchEffect`. Toto zajišťuje, že všechny reaktivní závislosti navštívené během normalizace v rámci `toValue()` jsou sledovány watcherem.

Tato verze `useFetch()` nyní přijímá statický URL string, refs i gettery, což ji dělá více fleibilní. `watchEffect()` se ihned spustí a bude sledovat jakékoli závislosti navštívené během `toValue(url)`. Pokud nejsou sledovány žádné závislosti (např. url už je statický string), efekt proběhne pouze jednou; jinak se spustí znovu pokaždé, když se sledovaná závislost změní.
Tato verze `useFetch()` nyní přijímá statický URL string, refs i gettery, což ji dělá více flexibilní. `watchEffect()` se ihned spustí a bude sledovat jakékoli závislosti navštívené během `toValue(url)`. Pokud nejsou sledovány žádné závislosti (např. URL už je statický string), efekt proběhne pouze jednou; jinak se spustí znovu pokaždé, když se sledovaná závislost změní.

Zde je [aktualizovaná verze `useFetch()`](https://play.vuejs.org/#eNp9Vdtu20YQ/ZUpUUA0qpAOjL4YktCbC7Rom8BN8sSHrMihtfZql9iLZEHgv2dml6SpxMiDIWkuZ+acmR2fs1+7rjgEzG6zlaut7Dw49KHbVFruO2M9nMFiu4Ta7LvgsYEeWmv2sKCkxSwoOPwTfb2b/EU5mopHR5GVro12HrbC4UerYA2Lnfeduy3LR2d0p0SNO6MatIU/dbI2DRZUtPSmMa4kgJQuG8qkjvLF28XVaAwRb2wxz69gvZkK/UQ5xUGogBQ/ZpyhEV4sAa01lnpeTwRyApsFWvT2RO6Eea40THBMgfq6NLwlS1/pVZnUJB3ph8c98fNIvwD+MaKBzkQut2xYbYP3RsPhTWvsusokSA0/Vxn8UitZP7GFSX/+8Sz7z1W2OZ9BQt+vypQXS1R+1cgDQciW4iMrimR0wu8270znfoC7SBaJWdAeLTa3QFgxuNijc+IBIy5PPyYOjU19RDEI954/Z/UptKTy6VvqA5XD1AwLTTl/0Aco4s5lV51F5sG+VJJ+v4qxYbmkfiiKYvSvyknPbJnNtoyW+HJpj4Icd22LtV+CN5/ikC4XuNL4HFPaoGsvie3FIqSJp1WIzabl00HxkoyetEVfufhv1kAu3EnX8z0CKEtKofcGzhMb2CItAELL1SPlFMV1pwVj+GROc/vWPoc26oDgdxhfSArlLnbWaBOcOoEzIP3CgbeifqLXLRyICaDBDnVD+3KC7emCSyQ4sifspOx61Hh4Qy/d8BsaOEdkYb1sZS2FoiJKnIC6FbqhsaTVZfk8gDgK6cHLPZowFGUzAQTNWl/BUSrFbzRYHXmSdeAp28RMsI0fyFDaUJg9Spd0SbERZcvZDBRleCPdQMCPh8ARwdRRnBCTjGz5WkT0i0GlSMqixTR6VKyHmmWEHIfV+naSOETyRx8vEYwMv7pa8dJU+hU9Kz2t86ReqjcgaTzCe3oGpEOeD4uyJOcjTXe+obScHwaAi82lo9dC/q/wuyINjrwbuC5uZrS4WAQeyTN9ftOXIVwy537iecoX92kR4q/F1UvqIMsSbq6vo5XF6ekCeEcTauVDFJpuQESvMv53IBXadx3r4KqMrt0w0kwoZY5/R5u3AZejvd5h/fSK/dE9s63K3vN7tQesssnnhX1An9x3//+Hz/R9cu5NExRFf8d5zyIF7jGF/RZ0Q23P4mK3f8XLRmfhg7t79qjdSIobjXLE+Cqju/b7d6i/tHtT3MQ8VrH/Ahstp5A=) s umělým zpožděním a náhodnými chybami pro demo účely.

Expand All @@ -252,7 +252,7 @@ Composable funkce se podle koncence pojmenovávají camelCase jmény, která za

### Vstupní parametry {#input-arguments}

Composable může přijímat parametry typu ref nebo gettery, i když se na ně při reaktivitě nespoléhá. Pokud píšete composable, který mohou používat i jiní vývojáři, je dobré ošetřit případ, kdy jsou namísto surových hodnot vstupními parametry ref nebo gettery. K tomuto účelu se bude hodit pomocná funkce [`toValue()`](/api/reactivity-utilities#tovalue):
Composable může přijímat parametry typu ref nebo gettery, i když se na ně při reaktivitě nespoléhá. Pokud píšete composable, který mohou používat i jiní vývojáři, je dobré ošetřit případ, kdy jsou namísto surových (raw) hodnot vstupními parametry ref nebo gettery. K tomuto účelu se bude hodit pomocná funkce [`toValue()`](/api/reactivity-utilities#tovalue):

```js
import { toValue } from 'vue'
Expand All @@ -265,7 +265,7 @@ function useFeature(maybeRefOrGetter) {
}
```

Pokud vaše composable vytváří reaktivní efekty, pokud je vstupem ref nebo getter, ujistěte se, že ref / getter buďto explicitně sledujete pomocí `watch()`, nebo uvnitř `watchEffect()` zavolejte `toValue()`, aby byl sledován správně.
Pokud vaše composable vytváří reaktivní efekty a vstupem je ref nebo getter, ujistěte se, že ref / getter buďto explicitně sledujete pomocí `watch()`, nebo uvnitř `watchEffect()` zavolejte `toValue()`, aby byl sledován správně.

Implementace [useFetch() představená dříve](#accepting-reactive-state) poskytuje konkrétní příklad composable, která přijímá refs, gettery i prosté hodnoty jako vstupní parametry.

Expand Down Expand Up @@ -294,11 +294,11 @@ Myš je na: {{ mouse.x }}, {{ mouse.y }}

### Vedlejší efekty {#side-effects}

Je v pořádku rovádět vedlejší efekty uvnitř composables (např. přidávat DOM event listenery nebo načítat data), dávejte však pozor na následující pravidla:
Je v pořádku provádět vedlejší efekty uvnitř composables (např. přidávat DOM event listenery nebo načítat data), dávejte však pozor na následující pravidla:

- Pokud pracujete na aplikaci, která používá [Server-side vykreslování](/guide/scaling-up/ssr) (SSR), ujistěte se, že vedlejší efekty specifické pro DOM jsou prováděny v lifecycle hooks po mount fázi, např. `onMounted()`. Tyto hooks jsou volány pouze v prohlížeči, takže si můžete být jisti, že kód uvnitř nich má k DOM přístup.
- Pokud pracujete na aplikaci, která používá [Vykreslování na serveru](/guide/scaling-up/ssr) (SSR), ujistěte se, že vedlejší efekty specifické pro DOM jsou prováděny v lifecycle hooks po mount fázi, např. `onMounted()`. Tyto hooks jsou volány pouze v prohlížeči, takže si můžete být jisti, že kód uvnitř nich má k DOM přístup.

- Nezapomeňte vedlejší účinky "uklidit" v `onUnmounted()`. Například pokud composable nastaví DOM event listener, měla by tento listener v `onUnmounted()` zase odstranit, jak jsme viděli v příkladu `useMouse()`. Může být užitečné použít composable, které to automaticky udělá za vás, jako je příklad `useEventListener()`.
- Nezapomeňte vedlejší účinky uklidit v `onUnmounted()`. Například pokud composable nastaví DOM event listener, měla by tento listener v `onUnmounted()` zase odstranit, jak jsme viděli v příkladu `useMouse()`. Může být užitečné použít composable, které to automaticky udělá za vás, jako je příklad `useEventListener()`.

### Omezení použití {#usage-restrictions}

Expand All @@ -308,7 +308,7 @@ Tato omezení jsou důležitá, protože se jedná o kontexty, ve kterých je Vu

1. Registrovat lifecycle hooks.

2. Nalinkovat computed proměnné a watchers, aby je při odstraňování (unmount) instance šlo zlikvidovat pro prevenci úniků paměti (memory leaks).
2. Nalinkovat computed proměnné a watchery, aby je při odstraňování (unmount) instance šlo zlikvidovat pro prevenci úniků paměti (memory leaks).

:::tip
`<script setup>` je jediné místo, kde můžete composables volat **po** použití příkazu `await`. Kompilátor za vás po asynchronní operaci automaticky obnoví kontext aktivní instance.
Expand Down Expand Up @@ -361,7 +361,7 @@ export default {

Uživatelé, kteří přicházejí od Vue 2, možná znají [mixins](/api/options-composition#mixins), které nám také umožňují extrahovat logiku komponent do opakovaně použitelných jednotek. Mixins mají tři hlavní nevýhody:

1. **Nejasný zdroj vlastností**: při použití mnoha mixins přestává být jasné, která vlastnost instance je implementována (injected) kterým, což ztěžuje sledování implementace a pochopení chování komponenty. To je také důvod, proč doporučujeme pro composables používat vzor refs + destrukturování: díky němu je zdroj vlastností v komponentách konzumentů zřejmý.
1. **Nejasný zdroj vlastností**: při použití mnoha mixins přestává být jasné, která vlastnost instance je implementována (injected) kterým, což ztěžuje sledování implementace a&nbsp;pochopení chování komponenty. To je také důvod, proč doporučujeme pro composables používat vzor refs + destrukturování: díky němu je zdroj vlastností v&nbsp;komponentách konzumentů zřejmý.

2. **Kolize jmenných prostorů**: mixins od různých autorů může potenciálně registrovat stejné klíče vlastností, což způsobuje kolize jmenných prostorů. U composables můžete přejmenovat destrukturované proměnné, pokud z různých composables přijdou konfliktní klíče.

Expand All @@ -370,7 +370,7 @@ Uživatelé, kteří přicházejí od Vue 2, možná znají [mixins](/api/option
Z výše uvedených důvodů již ve Vue 3 nedoporučujeme mixins používat. Funkce je zachována pouze pro učely migrace a kvůli obecné znalosti.
### vs. renderless komponenty {#vs-renderless-components}

V kapitole o slotech (slots) jsme se bavili o vzoru [renderless komponent](/guide/components/slots#renderless-components) založeném na scoped slotech. Dokonce jsme s použitím renderless komponenty implementovaly stejný příklad sledování pohybu myši.
V kapitole o slotech (slots) jsme se bavili o vzoru [komponent bez vykreslení](/guide/components/slots#renderless-components) založeném na scoped slotech. Dokonce jsme s použitím renderless komponenty implementovali stejný příklad sledování pohybu myši.

Hlavní výhodou composables oproti renderless komponentám je, že composables nemají dodatečnou režii v podobě instancí komponent. Při použití v celé aplikaci může množství dalších instancí komponent vytvořených vzorem renderless komponentám představovat znatelnou výkonnostní zátěž.

Expand All @@ -383,6 +383,6 @@ Pokud máte zkušenosti s Reactem, možná jste si všimli, že to vypadá velmi
## Další čtení {#further-reading}

- [Reaktivita podrobně](/guide/extras/reactivity-in-depth): pro pochopení fungování systému Vue reaktivity do větší hloubky na nižší úrovni.
- [State management](/guide/scaling-up/state-management): pro vzory správy stavu sdíleného více komponentami.
- [Správa stavu](/guide/scaling-up/state-management): pro vzory správy stavu sdíleného více komponentami.
- [Testování composables](/guide/scaling-up/testing#testing-composables): tipy na jednotkové testování composables.
- [VueUse](https://vueuse.org/): stále rostoucí kolekce Vue composables. Zdrojový kód je také skvělým zdrojem pro učení.
2 changes: 1 addition & 1 deletion src/guide/scaling-up/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ Nakonec aktualizujte `package.json` přidáním skriptu pro testování a spusť
> npm test
```

### Testování composables{#testovani-komponovatelnych-funkci}
### Testování composables{#testing-composables}

> Tato sekce předpokládá, že jste si přečetli kapitolu [Composables](/guide/reusability/composables).

Expand Down

0 comments on commit 98455e1

Please sign in to comment.