W Centrum Dowodzenia NOVA LAB nie wszystkie moduły interfejsu muszą mieć wizualną reprezentację. Niektóre odpowiadają wyłącznie za logikę — to renderless components. Poznajmy zaawansowane wzorce komponentów Vue.
Renderless component to komponent, który nie renderuje własnego HTML — zamiast tego udostępnia logikę przez slot. Cały wygląd definiuje komponent-rodzic.
1<script setup>
2import { ref, onMounted, onUnmounted } from 'vue'
3
4// Renderless component — logika zegara
5const now = ref(new Date())
6let timer = null
7
8onMounted(() => {
9 timer = setInterval(() => {
10 now.value = new Date()
11 }, 1000)
12})
13
14onUnmounted(() => clearInterval(timer))
15
16defineExpose({ now })
17</script>
18
19<template>
20 <slot :now="now" :formatted="now.toLocaleTimeString('pl-PL')" />
21</template>Użycie:
1<template>
2 <Clock v-slot="{ formatted }">
3 <div class="station-clock">{{ formatted }}</div>
4 </Clock>
5</template>Komponent
Clock nie ma własnego HTML — przekazuje dane przez scoped slot, a rodzic decyduje o wyglądzie.Popularny wzorzec — komponent zarządzający stanem toggle:
1<!-- Toggle.vue -->
2<script setup>
3import { ref } from 'vue'
4
5const isOn = ref(false)
6
7function toggle() {
8 isOn.value = !isOn.value
9}
10
11function setOn() { isOn.value = true }
12function setOff() { isOn.value = false }
13</script>
14
15<template>
16 <slot :isOn="isOn" :toggle="toggle" :setOn="setOn" :setOff="setOff" />
17</template>Użycie w różnych kontekstach:
1<template>
2 <!-- Jako przełącznik -->
3 <Toggle v-slot="{ isOn, toggle }">
4 <button @click="toggle">
5 System: {{ isOn ? 'AKTYWNY' : 'WYŁĄCZONY' }}
6 </button>
7 </Toggle>
8
9 <!-- Jako panel rozwijany -->
10 <Toggle v-slot="{ isOn, toggle }">
11 <div>
12 <h3 @click="toggle">Diagnostyka ▼</h3>
13 <div v-if="isOn">Szczegóły systemu...</div>
14 </div>
15 </Toggle>
16</template>W Vue 3 każdy komponent z
<script setup> jest już zoptymalizowany. Ale możesz też tworzyć proste komponenty funkcyjne jako zwykłe funkcje renderujące:1import { h } from 'vue'
2
3// Functional component — nie ma stanu, tylko props
4function StationBadge(props) {
5 return h('span', {
6 class: 'badge',
7 style: {
8 color: props.color || '#00ff88',
9 border: '1px solid ' + (props.color || '#00ff88'),
10 padding: '4px 12px',
11 borderRadius: '12px',
12 fontSize: '12px'
13 }
14 }, props.label)
15}
16
17StationBadge.props = ['label', 'color']Użycie w szablonie:
1<template>
2 <StationBadge label="ONLINE" color="#2ecc71" />
3 <StationBadge label="ALERT" color="#e74c3c" />
4</template>Funkcja
h() (hyperscript) tworzy VNode — wirtualny element DOM:1import { h } from 'vue'
2
3// h(tag, props, children)
4h('div', { class: 'panel' }, [
5 h('h2', null, 'Tytuł'),
6 h('p', null, 'Treść panelu')
7])Komponent z render function zamiast szablonu:
1import { h, ref, defineComponent } from 'vue'
2
3const Counter = defineComponent({
4 setup() {
5 const count = ref(0)
6
7 return () => h('div', { class: 'counter' }, [
8 h('p', null, 'Licznik: ' + count.value),
9 h('button', {
10 onClick: () => count.value++
11 }, '+1')
12 ])
13 }
14})HOC to funkcja, która przyjmuje komponent i zwraca nowy komponent z dodaną funkcjonalnością:
1import { h, ref, onMounted } from 'vue'
2
3function withLoading(WrappedComponent) {
4 return defineComponent({
5 setup(props, { attrs }) {
6 const loading = ref(true)
7
8 onMounted(() => {
9 setTimeout(() => {
10 loading.value = false
11 }, 1000)
12 })
13
14 return () => {
15 if (loading.value) {
16 return h('div', { class: 'loading' }, 'Ładowanie modułu...')
17 }
18 return h(WrappedComponent, { ...attrs })
19 }
20 }
21 })
22}Użycie:
1const MissionPanelWithLoading = withLoading(MissionPanel)| Wzorzec | Kiedy stosować | |---------|---------------| | Renderless component | Logika wielokrotnego użytku z elastycznym UI | | Functional component | Proste, bezstanowe elementy prezentacyjne | | Render function (h()) | Dynamiczne generowanie struktury DOM | | HOC | Dodawanie zachowania do istniejących komponentów |
Renderless components i scoped slots to idiomatyczny sposób Vue na separację logiki od prezentacji — szczególnie przydatny w systemach tak złożonych jak Centrum Dowodzenia NOVA LAB.