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 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 waterComposables 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}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>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}