Zaawansowane Vue 3 Composables: Wzorce wielokrotnego uzytku i pytania rekrutacyjne 2026
Kompleksowy przewodnik po zaawansowanych Vue 3 Composables z wzorcami wielokrotnego uzytku, asynchroniczna obsluga bledow, Dependency Injection, walidacja formularzy i aktualne pytania rekrutacyjne na 2026 rok.

Vue 3 Composables ugruntowaly swoja pozycje jako podstawowy mechanizm ekstrakcji i ponownego wykorzystania reaktywnej logiki pomiedzy komponentami. Wraz z wprowadzeniem Alien Signals i kompatybilnoscia z Vapor Mode w Vue 3.6, bieglosc w zakresie wzorcow Composable bezposrednio wplywa na architekture aplikacji, wydajnosc i utrzymywalnosc kodu w 2026 roku.
Composition API fundamentalnie zmienilo sposob, w jaki programisci organizuja i ponownie wykorzystuja logike w aplikacjach Vue. W centrum tej transformacji znajduja sie Composables: funkcje, ktore enkapsuluja reaktywny stan, logike biznesowa i efekty uboczne w niezalezne, wielokrotnie uzywane jednostki. W przeciwienstwie do mixinow z Vue 2, ktore powodowaly konflikty nazw i ukryte zaleznosci, Composables oferuja pelna przejrzystosc, statyczne typowanie dzieki TypeScript oraz jawna kompozycje.
Opanowanie zaawansowanych wzorcow Composable stalo sie w 2026 roku fundamentalnym wymaganiem na stanowiskach mid i senior w ekosystemie Vue. Niniejszy przewodnik analizuje wzorce najczesciej pojawiajace sie w technicznych rozmowach kwalifikacyjnych -- od anatomii dobrze zbudowanego Composable, przez Dependency Injection z uzyciem provide/inject, az po strategie testowania reaktywnosci kazdego elementu.
Composable to funkcja wykorzystujaca Composition API Vue do enkapsulacji i ponownego uzycia logiki zawierajacej stan. W przeciwienstwie do mixinow, Composables oferuja jawne wejscia i wyjscia, pelna inferencje typow TypeScript i brak jakichkolwiek kolizji nazw. Konwencja nakazuje prefiksowanie nazw Composable slowem use (np. useCounter, useFetch).
Anatomia dobrze zbudowanego Composable
Produkcyjny Composable podaza za przewidywalna struktura: przyjmuje konfiguracje przez argumenty, tworzy reaktywny stan wewnetrznie i udostepnia typowany obiekt zwrotny. Ten wzorzec gwarantuje kompozywalnosc, testowalnosc i czytelne granice API.
import { ref, computed, type Ref } from 'vue'
interface UseCounterOptions {
min?: number
max?: number
initialValue?: number
}
interface UseCounterReturn {
count: Ref<number>
doubled: Ref<number>
increment: () => void
decrement: () => void
reset: () => void
}
export function useCounter(options: UseCounterOptions = {}): UseCounterReturn {
const { min = 0, max = Infinity, initialValue = 0 } = options
const count = ref(initialValue)
const doubled = computed(() => count.value * 2)
function increment() {
if (count.value < max) count.value++
}
function decrement() {
if (count.value > min) count.value--
}
function reset() {
count.value = initialValue
}
return { count, doubled, increment, decrement, reset }
}Kilka aspektow tego wzorca zasluguje na szczegolna uwage. Interfejs UseCounterReturn jawnie definiuje kontrakt Composable, co ulatwia autouzupelnianie w edytorach kodu i zapobiega przypadkowym zmianom w publicznym API. Obiekt opcji z wartosciami domyslnymi umozliwia elastyczna konfiguracje bez przeladowywania sygnatury funkcji. Kazde wywolanie useCounter() tworzy niezalezna instancje stanu, co calkowicie eliminuje problemy ze wspoldzielonym stanem znane z mixinow.
Jawny typ zwrotny UseCounterReturn pelni podwojna role: dokumentuje publiczne API Composable i uniemozliwia przypadkowe ujawnienie wewnetrznych szczegolow implementacji. Komponenty konsumujace destrukturyzuja dokladnie to, czego potrzebuja, utrzymujac przejrzystosc wiazanw szablonie.
Asynchroniczne Composables z obsluga bledow i stanami ladowania
Pobieranie danych to jeden z najczestszych przypadkow uzycia Composables. Solidny asynchroniczny Composable zarzadza stanem ladowania, obsluga bledow i automatycznym czyszczeniem zasobow. Te wzorce sa regularnie sprawdzane podczas rozmow kwalifikacyjnych.
import { ref, watchEffect, onUnmounted, toValue, type Ref, type MaybeRefOrGetter } from 'vue'
interface UseFetchReturn<T> {
data: Ref<T | null>
error: Ref<string | null>
isLoading: Ref<boolean>
refresh: () => Promise<void>
}
export function useFetchData<T>(
url: MaybeRefOrGetter<string>
): UseFetchReturn<T> {
const data = ref<T | null>(null) as Ref<T | null>
const error = ref<string | null>(null)
const isLoading = ref(false)
let abortController: AbortController | null = null
async function fetchData() {
// Cancel any in-flight request
abortController?.abort()
abortController = new AbortController()
isLoading.value = true
error.value = null
try {
const response = await fetch(toValue(url), {
signal: abortController.signal
})
if (!response.ok) throw new Error(`HTTP ${response.status}`)
data.value = await response.json()
} catch (err) {
if (err instanceof DOMException && err.name === 'AbortError') return
error.value = err instanceof Error ? err.message : 'Unknown error'
} finally {
isLoading.value = false
}
}
// Re-fetch when URL changes reactively
watchEffect(() => {
fetchData()
})
onUnmounted(() => abortController?.abort())
return { data, error, isLoading, refresh: fetchData }
}Ten Composable wprowadza kilka zaawansowanych koncepcji. Typ parametru MaybeRefOrGetter<string> akceptuje zarowno zwykle stringi, jak i Refy czy funkcje getter, oferujac maksymalna elastycznosc dla wywolujacego. Funkcja toValue() ekstrahuje wartosc bazowa niezaleznie od typu wejscia. Wykorzystanie AbortController zapobiega wyscigom (race conditions), anulujac przestarzale zadania w momencie zmiany URL, zanim poprzednia odpowiedz zdazy nadejsc.
watchEffect ustanawia automatyczne reaktywne sledzenie: kazda Ref lub getter, do ktorych uzyskiwany jest dostep wewnatrz callbacka, automatycznie staje sie zaleznoscia. Gdy URL jest Refem i jego wartosc sie zmieni, efekt zostanie automatycznie ponownie wykonany. Hook onUnmounted gwarantuje czyszczenie zasobow po zniszczeniu komponentu, zapobiegajac wyciekom pamieci.
Composables wywolujace onMounted, onUnmounted lub inne hooki cyklu zycia musza byc wywolywane synchronicznie wewnatrz setup(). Wywolanie Composable wewnatrz asynchronicznego callbacka lub setTimeout spowoduje, ze hook cyklu zycia po cichu nie zostanie zarejestrowany, poniewaz nie istnieje aktywna instancja komponentu.
Kompozycja Composables: abstrakcje wyzszego poziomu
Prawdziwa sila Composables ujawnia sie, gdy komponuja one inne Composables. Odzwierciedla to zasade kompozycji funkcyjnej: male, skupione na jednym zadaniu jednostki lacza sie w zlozone zachowanie bez koniecznosci korzystania z hierarchii dziedziczenia.
import { ref, computed, watch, type Ref } from 'vue'
import { useFetchData } from './useFetchData'
import { useDebouncedRef } from './useDebouncedRef'
interface UsePaginatedSearchReturn<T> {
query: Ref<string>
page: Ref<number>
results: Ref<T[] | null>
totalPages: Ref<number>
isLoading: Ref<boolean>
error: Ref<string | null>
nextPage: () => void
prevPage: () => void
}
export function usePaginatedSearch<T>(
baseUrl: string,
perPage = 20
): UsePaginatedSearchReturn<T> {
const query = useDebouncedRef('', 300)
const page = ref(1)
const totalPages = ref(1)
const apiUrl = computed(
() => `${baseUrl}?q=${encodeURIComponent(query.value)}&page=${page.value}&limit=${perPage}`
)
const { data, error, isLoading } = useFetchData<{ items: T[]; total: number }>(apiUrl)
const results = computed(() => data.value?.items ?? null)
watch(data, (response) => {
if (response) {
totalPages.value = Math.ceil(response.total / perPage)
}
})
// Reset to page 1 when query changes
watch(query, () => { page.value = 1 })
function nextPage() {
if (page.value < totalPages.value) page.value++
}
function prevPage() {
if (page.value > 1) page.value--
}
return { query, page, results, totalPages, isLoading, error, nextPage, prevPage }
}Kompozycja odbywa sie na wielu poziomach. useDebouncedRef dostarcza Ref z wbudowanym debouncingiem, ktory zapobiega nadmiernym zadaniom podczas wpisywania tekstu. useFetchData otrzymuje apiUrl jako computed, co oznacza, ze kazda zmiana query lub page przelicza URL i automatycznie wyzwala nowe zadanie. Watcher na query resetuje paginacje do strony 1 przy kazdej zmianie frazy wyszukiwania.
Ten wzorzec demonstruje fundamentalna zasade: kazdy Composable rozwiazuje konkretny problem, a kompozycja laczy je bez tworzenia sprzezen. Podczas technicznej rozmowy kwalifikacyjnej wyjasnienie tego lancucha reaktywnosci (query sie zmienia -> apiUrl jest przeliczany -> useFetchData wykonuje ponowne zapytanie -> results sie aktualizuje) swiadczy o glebokim zrozumieniu systemu reaktywnosci Vue.
Gotowy na rozmowy o Vue.js / Nuxt.js?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Dependency Injection z provide/inject w architekturze Composables
Niektore stany musza byc wspoldzielone w obrebie drzewa komponentow bez koniecznosci przekazywania propsow przez kazdy poziom hierarchii. System provide/inject Vue w polaczeniu z Composables tworzy typowany i bezpieczny wzorzec kontekstu.
import { provide, inject, ref, readonly, type InjectionKey, type Ref } from 'vue'
type Theme = 'light' | 'dark' | 'system'
interface ThemeContext {
theme: Readonly<Ref<Theme>>
setTheme: (t: Theme) => void
resolvedTheme: Readonly<Ref<'light' | 'dark'>>
}
const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme')
export function provideTheme(initial: Theme = 'system') {
const theme = ref<Theme>(initial)
const resolvedTheme = computed<'light' | 'dark'>(() => {
if (theme.value !== 'system') return theme.value
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
})
function setTheme(t: Theme) {
theme.value = t
}
const context: ThemeContext = {
theme: readonly(theme),
setTheme,
resolvedTheme: readonly(resolvedTheme)
}
provide(ThemeKey, context)
return context
}
export function useTheme(): ThemeContext {
const context = inject(ThemeKey)
if (!context) {
throw new Error('useTheme() requires a parent component to call provideTheme()')
}
return context
}Ten wzorzec oddziela dostarczanie kontekstu od jego konsumpcji. Komponent glowny (lub layout) wywoluje provideTheme() jednokrotnie, a kazdy komponent potomny moze uzyskac dostep do kontekstu przez useTheme(), niezaleznie od glebokosci w drzewie komponentow. Uzycie InjectionKey<ThemeContext> z Symbol gwarantuje bezpieczenstwo typow w czasie kompilacji.
Funkcja readonly() opakowuje eksponowane Refy, aby zapobiec bezposrednim mutacjom przez komponenty konsumujace. Jedynie setTheme() moze modyfikowac motyw, wymuszajac jednokierunkowy przeplyw danych. Wlasciwosc resolvedTheme rozwiazuje wartosc 'system' przez odpytanie media query przegladarki, dostarczajac zawsze konkretna wartosc ('light' lub 'dark'), ktora komponenty moga bezposrednio uzyc do zastosowania stylow.
Podczas rozmow rekrutacyjnych ten wzorzec jest czesto porownywany z React Context lub storami Pinia. Kluczowa roznica polega na tym, ze provide/inject dziala na poziomie drzewa komponentow (nie globalnie) i nie wymaga zewnetrznych zaleznosci. Daje to precyzyjna kontrole nad zakresem wspoldzielonego stanu.
Composable do walidacji formularzy
Walidacja formularzy to obszar, w ktorym Composables szczegolnie sie sprawdzaja. Zastepuja powtarzalna logike deklaratywnym, opartym na regulach wzorcem, ktory mozna ponownie wykorzystac w calej aplikacji.
import { reactive, computed, type UnwrapNestedRefs } from 'vue'
type ValidationRule<T> = (value: T) => string | true
type FieldRules<T> = { [K in keyof T]?: ValidationRule<T[K]>[] }
interface UseFormReturn<T extends Record<string, any>> {
fields: UnwrapNestedRefs<T>
errors: Record<keyof T, string>
isValid: Ref<boolean>
validate: () => boolean
resetErrors: () => void
}
export function useFormValidation<T extends Record<string, any>>(
initialValues: T,
rules: FieldRules<T>
): UseFormReturn<T> {
const fields = reactive({ ...initialValues }) as UnwrapNestedRefs<T>
const errors = reactive(
Object.keys(initialValues).reduce(
(acc, key) => ({ ...acc, [key]: '' }),
{} as Record<keyof T, string>
)
)
function validate(): boolean {
let valid = true
for (const key of Object.keys(rules) as (keyof T)[]) {
const fieldRules = rules[key] || []
errors[key] = '' as any
for (const rule of fieldRules) {
const result = rule(fields[key])
if (result !== true) {
errors[key] = result as any
valid = false
break // Stop at first error per field
}
}
}
return valid
}
function resetErrors() {
for (const key of Object.keys(errors)) {
(errors as any)[key] = ''
}
}
const isValid = computed(() =>
Object.values(errors).every((e) => e === '')
)
return { fields, errors, isValid, validate, resetErrors }
}Composable przyjmuje wartosci poczatkowe oraz mape regul walidacji dla kazdego pola. Kazda regula to funkcja zwracajaca true w przypadku poprawnej wartosci lub string z komunikatem bledu. Metoda validate() iteruje po regulach i zatrzymuje sie na pierwszym bledzie kazdego pola, aby uniknac wyswietlania wielu komunikatow jednoczesnie, co mogloby dezorientowac uzytkownikow.
Uzycie reactive() zamiast ref() dla pol i bledow upraszcza dostep w szablonach: fields.email zamiast fields.value.email. Obliczana wlasciwosc isValid aktualizuje sie automatycznie przy kazdej zmianie bledu, umozliwiajac reaktywne wlaczanie lub wylaczanie przyciskow wysylania formularza.
Ten wzorzec jest latwo rozszerzalny. Asynchroniczne reguly (sprawdzanie dostepnosci adresu e-mail), walidacja miedzypolowa (potwierdzenie hasla) oraz zinternacjonalizowane komunikaty bledow moga byc dodane bez modyfikacji podstawowej struktury Composable. W praktyce produkcyjnej czesto laczy sie ten wzorzec z biblioteka typu Zod do definicji schematow walidacji.
Testowanie Composables w izolacji
Composables wykorzystujace reaktywne API Vue wymagaja kontekstu komponentu do prawidlowego dzialania. Funkcja pomocnicza withSetup rozwiazuje to wymaganie, tworzac minimalna komponent, ktory wykonuje Composable wewnatrz setup().
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { defineComponent, h } from 'vue'
import { useCounter } from './useCounter'
function withSetup<T>(composable: () => T): { result: T; unmount: () => void } {
let result!: T
const wrapper = mount(
defineComponent({
setup() {
result = composable()
return () => h('div')
}
})
)
return { result, unmount: () => wrapper.unmount() }
}
describe('useCounter', () => {
it('initializes with default value', () => {
const { result } = withSetup(() => useCounter())
expect(result.count.value).toBe(0)
})
it('respects min and max boundaries', () => {
const { result } = withSetup(() =>
useCounter({ min: 0, max: 3, initialValue: 3 })
)
result.increment()
expect(result.count.value).toBe(3) // Capped at max
result.count.value = 0
result.decrement()
expect(result.count.value).toBe(0) // Capped at min
})
it('computes doubled value reactively', () => {
const { result } = withSetup(() => useCounter({ initialValue: 5 }))
expect(result.doubled.value).toBe(10)
result.increment()
expect(result.doubled.value).toBe(12)
})
})Funkcja withSetup jest standardowym wzorcem w ekosystemie Vue do testowania Composables w izolacji. Montuje komponent opakowujacy, ktory wywoluje Composable wewnatrz setup(), i zapewnia dostep do wyniku oraz funkcji unmount() do symulacji zniszczenia komponentu.
Kazdy test waliduje konkretny aspekt: inicjalizacje z wartosciami domyslnymi, przestrzeganie skonfigurowanych granic oraz reaktywnosc wartosci obliczanych. Trzeci test jest szczegolnie istotny, poniewaz weryfikuje, ze doubled automatycznie sie aktualizuje po zmianie count przez increment(), potwierdzajac prawidlowe dzialanie grafu reaktywnosci.
W przypadku asynchronicznych Composables, takich jak useFetchData, testy wymagaja mockowania fetch oraz obslugi promisow za pomoca flushPromises() z Vue Test Utils. Podczas rozmow kwalifikacyjnych znajomosc tego wzorca testowania swiadczy o profesjonalnej dojrzalosci i dbaniu o jakosc kodu.
VueUse zawiera ponad 200 produkcyjnych Composables obejmujacych API przegladarki, sensory, animacje i funkcje pomocnicze. Analiza kodu zrodlowego ujawnia spojne wzorce: obiekty opcji do konfiguracji, zabezpieczenia SSR i tryOnScopeDispose do czyszczenia zasobow. Biblioteka stanowi doskonale zrodlo referencyjne przy podejmowaniu decyzji architektonicznych dotyczacych Composables.
Najczestsze pytania rekrutacyjne dotyczace Vue 3 Composables
Ponizsze pytania regularnie pojawiaja sie w procesach rekrutacyjnych na stanowiska mid i senior Vue w 2026 roku:
1. Jaka jest roznica miedzy Composable a mixinem?
Mixiny wstrzykuja wlasciwosci do komponentu w sposob niejawny, co prowadzi do konfliktow nazw i utrudnia sledzenie pochodzenia kazdej wlasciwosci. Composables zwracaja wartosci jawnie, oferuja pelne typowanie TypeScript i umozliwiaja zmiane nazw zmiennych podczas destrukturyzacji zwracanego obiektu. Kazde wywolanie tworzy niezalezna instancje stanu, podczas gdy mixiny wspoldziela stan miedzy wszystkimi komponentami, ktore je wlaczaja.
2. Dlaczego Composables musza byc wywolywane wewnatrz setup()?
Vue kojarzy hooki cyklu zycia (onMounted, onUnmounted, watch, watchEffect) z aktywna instancja komponentu podczas wykonywania setup(). Wywolanie Composable poza tym kontekstem przerywa te asocjacje, wskutek czego efekty nie zostana zarejestrowane ani wyczyszczone. Jest to jedno z najczesciej zadawanych pytan w technicznych rozmowach kwalifikacyjnych dotyczacych Vue.
3. Jak wspoldzielic stan miedzy wieloma komponentami za pomoca Composables?
Istnieja dwie podstawowe strategie. Dla stanu globalnego Ref deklaruje sie poza funkcja Composable (wzorzec Singleton). Dla stanu kontekstowego w poddrzewie komponentow stosuje sie provide/inject z typowanym InjectionKey, jak zademonstrowano w przykladzie useTheme. W przypadku bardziej zlozonego globalnego zarzadzania stanem rekomendowane jest uzycie Pinia.
4. Czym jest MaybeRefOrGetter i dlaczego jest wazny?
Jest to typ narzeddziowy Vue, ktory akceptuje zwykla wartosc, Ref lub funkcje getter. Umozliwia Composables przyjmowanie zarowno statycznych, jak i reaktywnych danych wejsciowych, maksymalizujac elastycznosc dla wywolujacego. Funkcja toValue() ekstrahuje wartosc bazowa niezaleznie od typu wejscia.
5. Jak testowac Composables korzystajace z onMounted lub onUnmounted?
Stosuje sie funkcje pomocnicza typu withSetup, ktora montuje komponent opakowujacy. Ten komponent wykonuje Composable wewnatrz setup(), zapewniajac wymagany kontekst instancji. W celu weryfikacji czyszczenia zasobow wywoluje sie unmount() i sprawdza, czy efekty uboczne (listenery, timery, AbortController) zostaly usuniete.
6. Kiedy nalezy uzyc Pinia zamiast Composables z provide/inject?
Pinia jest preferowana, gdy stan musi byc globalnie dostepny z dowolnego komponentu bez relacji hierarchicznej, gdy wymagana jest persystencja (localStorage/sessionStorage), gdy potrzebna jest integracja z Vue DevTools do debugowania lub gdy wiele drzew komponentow musi wspoldzielic ten sam stan. Composables z provide/inject sa lepiej dopasowane do stanu kontekstowego ograniczonego do okreslonego poddrzewa komponentow.
Podsumowanie
Vue 3 Composables to znacznie wiecej niz alternatywa dla mixinow: stanowia paradygmat organizacji kodu, ktory skaluje sie od prostych funkcji pomocniczych po zlozone systemy z Dependency Injection i wielopoziomowa kompozycja. Opanowanie tych wzorcow odroznia programistow, ktorzy jedynie uzywaja Vue, od tych, ktorzy rozumieja system reaktywnosci w glab.
Kluczowe wnioski:
- Czytelna struktura: Typowane interfejsy wejscia i wyjscia (
UseXxxOptionsiUseXxxReturn) dokumentuja API i zapobiegaja przypadkowemu ujawnieniu wewnetrznych szczegolow implementacji - Izolacja stanu: Kazde wywolanie tworzy niezalezna instancje, calkowicie eliminujac konflikty
- Jawna kompozycja: Composables konsumujace inne Composables tworca przewidywalne lancuchy reaktywnosci
- Zarzadzanie zasobami:
AbortControllerionUnmountedgwarantuja czyszczenie efektow ubocznych i zapobiegaja wyciekom pamieci - Typowana iniekcja:
provide/injectzInjectionKeyzastepuje prop drilling bez utraty bezpieczenstwa typow - Testowalnosc: Wzorzec
withSetupumozliwia walidacje reaktywnosci w pelnej izolacji - Pragmatyzm: Sprawdzenie VueUse przed reimplementacja typowych funkcjonalnosci oszczedza czas i wykorzystuje sprawdzone rozwiazania
Przygotowanie tych wzorcow z konkretnymi implementacjami oraz zdolnoscia do wyjasnienia decyzji projektowych stojacych za kazdym z nich daje znaczaca przewage w technicznych procesach rekrutacyjnych na stanowiska Vue w 2026 roku.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Tagi
Udostępnij
Powiązane artykuły

Nuxt 4 w 2026: Nowa Struktura Katalogow i Migracja z Nuxt 3
Kompletny przewodnik po Nuxt 4: nowa struktura katalogu app/, migracja krok po kroku z Nuxt 3, singleton data fetching, shallow reactivity, TypeScript context splitting, Vue Router v5, zarzadzanie meta tagami i lista kontrolna migracji.

Vue 3 Pinia vs Vuex: Nowoczesne zarządzanie stanem i pytania rekrutacyjne 2026
Porównanie Pinia i Vuex: architektura, TypeScript, Composition API, migracja, hydratacja SSR oraz najczęstsze pytania rekrutacyjne o zarządzanie stanem Vue na rok 2026.

Kluczowe pytania rekrutacyjne Vue.js: 25 pytań na zdobycie pracy
Przygotowanie do rozmów Vue.js dzięki 25 kluczowym pytaniom. Od reaktywności po composables, opanuj najważniejsze pojęcia przed kolejną rozmową.