Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Zaawansowane wzorce Composables

Witaj w Laboratorium Naukowym NOVA LAB! Znasz juz podstawy composables - teraz czas na zaawansowane wzorce projektowe. Jak inzynierowie NOVA LAB projektuja modulowe systemy stacji kosmicznej, tak my zaprojektujemy composables gotowe na kazde wyzwanie.

Factory Composables

Factory composable to funkcja, ktora tworzy composables z rozna konfiguracją - jak fabryka modułów stacji, gdzie kazdy moduł ma te same interfejsy, ale rozne parametry.

1import { ref, computed } from 'vue'
2
3// Factory: tworzy composable dla dowolnego zasobu
4function createResourceManager(resourceName, maxCapacity = 100) {
5  const currentLevel = ref(maxCapacity)
6  const isLow = computed(() => currentLevel.value < maxCapacity * 0.2)
7  const isCritical = computed(() => currentLevel.value < maxCapacity * 0.05)
8  const percentage = computed(() =>
9    Math.round((currentLevel.value / maxCapacity) * 100)
10  )
11
12  function consume(amount) {
13    currentLevel.value = Math.max(0, currentLevel.value - amount)
14  }
15
16  function refill(amount) {
17    currentLevel.value = Math.min(maxCapacity, currentLevel.value + amount)
18  }
19
20  return {
21    resourceName,
22    currentLevel,
23    maxCapacity,
24    percentage,
25    isLow,
26    isCritical,
27    consume,
28    refill
29  }
30}
31
32// Uzycie - kazdy zasob ma te same metody, ale niezalezny stan
33const oxygen = createResourceManager('Oxygen', 500)
34const fuel = createResourceManager('Fuel', 1000)
35const water = createResourceManager('Water', 200)
36
37oxygen.consume(50) // nie wplywa na fuel ani water

Kompozycja Composables

Composables moga uzywac innych composables - to jak laczenie modułów stacji w wieksze systemy.

1import { ref, computed, watch } from 'vue'
2
3// Bazowy composable
4function useNotifications() {
5  const notifications = ref([])
6
7  function addNotification(message, type = 'info') {
8    notifications.value.unshift({
9      id: Date.now(),
10      message,
11      type,
12      timestamp: new Date()
13    })
14    // Ogranicz do 50 notyfikacji
15    if (notifications.value.length > 50) {
16      notifications.value.pop()
17    }
18  }
19
20  function clearAll() {
21    notifications.value = []
22  }
23
24  return { notifications, addNotification, clearAll }
25}
26
27// Composable korzystajacy z innego composable
28function useLabMonitor(thresholds) {
29  const temperature = ref(22)
30  const pressure = ref(101)
31  const { notifications, addNotification, clearAll } = useNotifications()
32
33  watch(temperature, (val) => {
34    if (val > thresholds.maxTemp) {
35      addNotification(
36        `Temperatura krytyczna: ${val}°C!`,
37        'danger'
38      )
39    }
40  })
41
42  watch(pressure, (val) => {
43    if (val < thresholds.minPressure) {
44      addNotification(
45        `Cisnienie ponizej normy: ${val} kPa`,
46        'warning'
47      )
48    }
49  })
50
51  return {
52    temperature,
53    pressure,
54    notifications,
55    addNotification,
56    clearAll
57  }
58}

Dependency Injection w Composables

Vue 3 pozwala na wstrzykiwanie zaleznosci przez

provide/inject
- to jak centralne systemy stacji dostepne dla kazdego modulu.

1import { ref, provide, inject } from 'vue'
2
3// Symbol jako klucz - unikamy kolizji nazw
4const MissionContextKey = Symbol('MissionContext')
5
6// Composable dostarczajacy kontekst
7function provideMissionContext() {
8  const missionName = ref('Mars Exploration 2087')
9  const crewCount = ref(6)
10  const missionDay = ref(1)
11
12  const context = {
13    missionName,
14    crewCount,
15    missionDay,
16    advanceDay() { missionDay.value++ }
17  }
18
19  provide(MissionContextKey, context)
20  return context
21}
22
23// Composable konsumujacy kontekst
24function useMissionContext() {
25  const context = inject(MissionContextKey)
26  if (!context) {
27    throw new Error(
28      'useMissionContext wymaga provideMissionContext w komponencie nadrzednym'
29    )
30  }
31  return context
32}
1<!-- ParentComponent.vue -->
2<script setup>
3const { missionName, missionDay, advanceDay } = provideMissionContext()
4</script>
5
6<!-- ChildComponent.vue (dowolna glebokosc zagniezdzenia) -->
7<script setup>
8const { missionName, missionDay } = useMissionContext()
9// Automatycznie reaktywne - zmiana w rodzicu aktualizuje dzieci
10</script>

Obsługa Bledów w Composables

Kazdy system stacji musi miec zabezpieczenia na wypadek awarii.

1import { ref, computed } from 'vue'
2
3function useAsyncOperation(asyncFn) {
4  const data = ref(null)
5  const error = ref(null)
6  const loading = ref(false)
7  const retryCount = ref(0)
8  const MAX_RETRIES = 3
9
10  const hasError = computed(() => error.value !== null)
11  const canRetry = computed(() => retryCount.value < MAX_RETRIES)
12
13  async function execute(...args) {
14    loading.value = true
15    error.value = null
16
17    try {
18      data.value = await asyncFn(...args)
19      retryCount.value = 0 // Reset po sukcesie
20    } catch (e) {
21      error.value = e.message || 'Nieznany blad'
22      console.error(`Operacja nieudana: ${error.value}`)
23    } finally {
24      loading.value = false
25    }
26  }
27
28  async function retry(...args) {
29    if (!canRetry.value) {
30      error.value = 'Przekroczono limit prob'
31      return
32    }
33    retryCount.value++
34    await execute(...args)
35  }
36
37  return { data, error, loading, hasError, canRetry, retryCount, execute, retry }
38}
39
40// Uzycie
41const sensorData = useAsyncOperation(async (sensorId) => {
42  const response = await fetch(`/api/sensors/${sensorId}`)
43  if (!response.ok) throw new Error('Blad pobierania danych')
44  return response.json()
45})
46
47// W komponencie
48await sensorData.execute('temp-sensor-1')
49if (sensorData.hasError.value && sensorData.canRetry.value) {
50  await sensorData.retry('temp-sensor-1')
51}
Vai a CodeWorlds