W centrum kontroli NOVA LAB kazdy system musi byc niezawodny i przewidywalny. Wzorce projektowe z reaktywnoscia Vue 3 pozwalaja budowac takie systemy - jak sprawdzone protokoly inzynieryjne stacji kosmicznej.
Czasem potrzebujemy jednej wspolnej instancji stanu - jak centralny komputer stacji, z ktorego korzystaja wszystkie moduly.
1import { reactive, readonly } from 'vue'
2
3// Stan tworzony RAZ, poza funkcja
4const globalState = reactive({
5 currentUser: null,
6 theme: 'dark',
7 locale: 'pl',
8 notifications: []
9})
10
11// Composable zwraca ten sam stan za kazdym razem
12export function useGlobalStore() {
13 function setUser(user) {
14 globalState.currentUser = user
15 }
16
17 function setTheme(theme) {
18 globalState.theme = theme
19 }
20
21 function addNotification(msg) {
22 globalState.notifications.push({
23 id: Date.now(),
24 message: msg,
25 read: false
26 })
27 }
28
29 // readonly zapobiega bezposredniej modyfikacji
30 return {
31 state: readonly(globalState),
32 setUser,
33 setTheme,
34 addNotification
35 }
36}1<!-- Dowolny komponent - ten sam stan -->
2<script setup>
3import { useGlobalStore } from './composables/useGlobalStore'
4
5// Oba komponenty widza te same dane
6const { state, addNotification } = useGlobalStore()
7</script>
8
9<template>
10 <p>Motyw: {{ state.theme }}</p>
11 <p>Powiadomienia: {{ state.notifications.length }}</p>
12</template>Kluczowa roznica: stan jest zdefiniowany poza funkcja composable, wiec kazde wywolanie
useGlobalStore() zwraca referencje do tego samego obiektu.Zamiast klasycznego event busa, mozemy stworzyc reaktywny system zdarzen - jak siec komunikacyjna stacji NOVA LAB.
1import { ref, watch } from 'vue'
2
3function useEventBus() {
4 const lastEvent = ref(null)
5 const listeners = new Map()
6
7 function emit(eventName, payload) {
8 lastEvent.value = { name: eventName, payload, timestamp: Date.now() }
9
10 const handlers = listeners.get(eventName) || []
11 handlers.forEach(handler => handler(payload))
12 }
13
14 function on(eventName, handler) {
15 if (!listeners.has(eventName)) {
16 listeners.set(eventName, [])
17 }
18 listeners.get(eventName).push(handler)
19
20 // Zwroc funkcje unsubscribe
21 return () => {
22 const handlers = listeners.get(eventName)
23 const index = handlers.indexOf(handler)
24 if (index > -1) handlers.splice(index, 1)
25 }
26 }
27
28 return { lastEvent, emit, on }
29}1<script setup>
2import { onUnmounted } from 'vue'
3
4const { emit, on } = useEventBus()
5
6// Subskrypcja z automatycznym cleanup
7const unsubscribe = on('sensor-alert', (data) => {
8 console.log('Alert z sensora:', data)
9})
10
11onUnmounted(() => {
12 unsubscribe() // Wazne! Zapobiegamy wyciekom pamieci
13})
14
15// Emitowanie zdarzenia
16emit('sensor-alert', { sensorId: 'T-01', value: 45 })
17</script>Automaty stanowe to idealny wzorzec do zarzadzania zlozonym stanem - jak protokoly bezpieczenstwa stacji, gdzie kazdy stan ma dokladnie okreslone przejscia.
1import { ref, computed } from 'vue'
2
3function useStateMachine(config) {
4 const currentState = ref(config.initial)
5
6 const availableTransitions = computed(() => {
7 const stateConfig = config.states[currentState.value]
8 return stateConfig ? Object.keys(stateConfig.on || {}) : []
9 })
10
11 function transition(event) {
12 const stateConfig = config.states[currentState.value]
13 if (!stateConfig || !stateConfig.on || !stateConfig.on[event]) {
14 console.warn(
15 `Niedozwolone przejscie: ${currentState.value} -> ${event}`
16 )
17 return false
18 }
19
20 const nextState = stateConfig.on[event]
21 currentState.value = nextState
22 return true
23 }
24
25 function canTransition(event) {
26 return availableTransitions.value.includes(event)
27 }
28
29 return { currentState, availableTransitions, transition, canTransition }
30}1<script setup>
2const experimentMachine = useStateMachine({
3 initial: 'idle',
4 states: {
5 idle: {
6 on: { START: 'running', CONFIGURE: 'configuring' }
7 },
8 configuring: {
9 on: { SAVE: 'idle', CANCEL: 'idle' }
10 },
11 running: {
12 on: { PAUSE: 'paused', COMPLETE: 'completed', FAIL: 'failed' }
13 },
14 paused: {
15 on: { RESUME: 'running', ABORT: 'failed' }
16 },
17 completed: {
18 on: { RESET: 'idle' }
19 },
20 failed: {
21 on: { RESET: 'idle', RETRY: 'running' }
22 }
23 }
24})
25
26// Uzywamy w template
27// experimentMachine.currentState.value => 'idle'
28// experimentMachine.transition('START') => stan zmienia sie na 'running'
29// experimentMachine.canTransition('PAUSE') => true
30</script>Wzorzec obserwatora z reaktywnoscia Vue - jak system monitorowania, gdzie kazdy sensor moze miec wielu obserwatorow.
1import { ref, watch } from 'vue'
2
3function useObservable(initialValue) {
4 const value = ref(initialValue)
5 const history = ref([])
6 const observers = ref(0)
7
8 // Kazdy watch to nowy obserwator
9 function subscribe(callback) {
10 observers.value++
11 const unwatch = watch(value, (newVal, oldVal) => {
12 callback({ newVal, oldVal, timestamp: Date.now() })
13 })
14
15 return () => {
16 unwatch()
17 observers.value--
18 }
19 }
20
21 function setValue(newValue) {
22 history.value.push({
23 from: value.value,
24 to: newValue,
25 timestamp: Date.now()
26 })
27 if (history.value.length > 100) history.value.shift()
28 value.value = newValue
29 }
30
31 return {
32 value: readonly(value),
33 history: readonly(history),
34 observers,
35 subscribe,
36 setValue
37 }
38}1<script setup>
2import { readonly, onUnmounted } from 'vue'
3
4const temperature = useObservable(22)
5
6// Obserwator 1 - alarm
7const unsub1 = temperature.subscribe(({ newVal }) => {
8 if (newVal > 40) console.warn('Temperatura krytyczna!')
9})
10
11// Obserwator 2 - logowanie
12const unsub2 = temperature.subscribe(({ newVal, oldVal }) => {
13 console.log(`Temp: ${oldVal} -> ${newVal}`)
14})
15
16onUnmounted(() => {
17 unsub1()
18 unsub2()
19})
20
21// Zmiana wartosci powiadamia wszystkich obserwatorow
22temperature.setValue(25)
23</script>
24
25<template>
26 <p>Temperatura: {{ temperature.value }}°C</p>
27 <p>Aktywni obserwatorzy: {{ temperature.observers }}</p>
28 <p>Historia zmian: {{ temperature.history.length }}</p>
29</template>