W stacji NOVA LAB kazdy wat energii sie liczy - tak samo w aplikacjach Vue kazdy niepotrzebny render i obliczenie to strata wydajnosci. Poznajmy techniki optymalizacji composables.
ref() tworzy gleboka reaktywnosc - kazda zmiana wewnatrz obiektu powoduje aktualizacje. shallowRef() reaguje tylko na zmiane calej wartosci, nie wewnetrznych wlasciwosci.1import { shallowRef, triggerRef } from 'vue'
2
3// Duza lista danych - shallowRef jest wydajniejszy
4const sensorReadings = shallowRef([])
5
6// To NIE wywoła aktualizacji (plytka reaktywnosc)
7sensorReadings.value.push({ id: 1, temp: 22 })
8
9// To wywoła aktualizacje - przypisanie nowej tablicy
10sensorReadings.value = [...sensorReadings.value, { id: 1, temp: 22 }]
11
12// Alternatywnie: reczne wyzwolenie aktualizacji
13sensorReadings.value.push({ id: 2, temp: 23 })
14triggerRef(sensorReadings) // Wymus aktualizacje1<script setup>
2import { shallowRef, triggerRef, computed } from 'vue'
3
4// Wydajny composable dla duzych zbiorow danych
5function useLargeDataset() {
6 const items = shallowRef([])
7 const count = computed(() => items.value.length)
8
9 function addItem(item) {
10 // Tworzymy nowa tablice - wyzwala reaktywnosc
11 items.value = [...items.value, item]
12 }
13
14 function addMany(newItems) {
15 // Jedna aktualizacja zamiast N
16 items.value = [...items.value, ...newItems]
17 }
18
19 function removeItem(id) {
20 items.value = items.value.filter(item => item.id !== id)
21 }
22
23 function updateItem(id, updates) {
24 items.value = items.value.map(item =>
25 item.id === id ? { ...item, ...updates } : item
26 )
27 }
28
29 return { items, count, addItem, addMany, removeItem, updateItem }
30}
31</script>Kiedy uzywac
shallowRef:Computed properties sa automatycznie cachowane - przeliczaja sie tylko gdy zmienia sie ich zaleznosc. To kluczowa optymalizacja.
1import { ref, computed } from 'vue'
2
3function useFilteredExperiments() {
4 const experiments = ref([])
5 const searchQuery = ref('')
6 const category = ref('all')
7
8 // DOBRZE: computed jest cachowane
9 // Przelicza sie TYLKO gdy experiments, searchQuery lub category sie zmienia
10 const filtered = computed(() => {
11 console.log('Przeliczam filtrowane eksperymenty') // loguje rzadko
12 let result = experiments.value
13
14 if (searchQuery.value) {
15 const q = searchQuery.value.toLowerCase()
16 result = result.filter(e =>
17 e.name.toLowerCase().includes(q)
18 )
19 }
20
21 if (category.value !== 'all') {
22 result = result.filter(e => e.category === category.value)
23 }
24
25 return result
26 })
27
28 // ZLE: funkcja przelicza za kazdym renderem
29 function getFiltered() {
30 console.log('Przeliczam za kazdym razem!') // loguje czesto
31 return experiments.value.filter(...)
32 }
33
34 // DOBRZE: laczenie computed w lancuch
35 const stats = computed(() => ({
36 total: filtered.value.length,
37 active: filtered.value.filter(e => e.status === 'active').length,
38 completed: filtered.value.filter(e => e.status === 'completed').length
39 }))
40
41 return { experiments, searchQuery, category, filtered, stats }
42}watch i watchEffect roznia sie nie tylko API, ale tez wydajnoscia.1import { ref, watch, watchEffect } from 'vue'
2
3const temperature = ref(22)
4const pressure = ref(101)
5const humidity = ref(45)
6
7// watchEffect - sledzi WSZYSTKIE uzyyte reaktywne wartosci
8// Odpala sie przy zmianie temperature, pressure LUB humidity
9watchEffect(() => {
10 console.log('watchEffect:', temperature.value)
11 // Nawet jesli uzywasz pressure tylko w warunku,
12 // watchEffect sledzi je ZAWSZE
13 if (temperature.value > 30) {
14 console.log('Cisnienie:', pressure.value)
15 }
16})
17
18// watch - sledzi TYLKO okreslone zrodlo
19// Odpala sie TYLKO przy zmianie temperature
20watch(temperature, (newTemp) => {
21 console.log('watch:', newTemp)
22 // Mozesz czytac inne refs bez tworzenia zaleznosci
23 if (newTemp > 30) {
24 console.log('Cisnienie:', pressure.value)
25 }
26})Reguła: uzywaj
watch gdy potrzebujesz reagowac na konkretne zmiany. Uzywaj watchEffect gdy chcesz automatycznego sledzenia wielu zaleznosci.Jeden z najczestszych problemow - niesprzątnięte zasoby powoduja wycieki pamieci.
1import { ref, onMounted, onUnmounted, watch } from 'vue'
2
3// ZLE - wyciek pamieci!
4function useLeakyTimer() {
5 const seconds = ref(0)
6
7 onMounted(() => {
8 setInterval(() => { // Nigdy nie czyścimy!
9 seconds.value++
10 }, 1000)
11 })
12
13 return { seconds }
14}
15
16// DOBRZE - poprawny cleanup
17function useSafeTimer() {
18 const seconds = ref(0)
19 const isRunning = ref(false)
20 let intervalId = null
21
22 function start() {
23 if (isRunning.value) return
24 isRunning.value = true
25 intervalId = setInterval(() => {
26 seconds.value++
27 }, 1000)
28 }
29
30 function stop() {
31 if (!isRunning.value) return
32 isRunning.value = false
33 clearInterval(intervalId)
34 intervalId = null
35 }
36
37 // Zawsze czyscimy w onUnmounted
38 onUnmounted(() => {
39 if (intervalId) {
40 clearInterval(intervalId)
41 }
42 })
43
44 return { seconds, isRunning, start, stop }
45}| Technika | Kiedy uzywac | |----------|-------------| |
shallowRef | Duze tablice/obiekty, rzadkie aktualizacje |
| computed zamiast funkcji | Wartosc zalezy od reactive state |
| watch zamiast watchEffect | Konkretne zrodlo do obserwacji |
| Cleanup w onUnmounted | Timery, event listeners, WebSocket |
| Batch updates | Wiele zmian naraz zamiast po jednej |
| triggerRef | Mutacja shallowRef bez tworzenia kopii |1// Batch updates - jedna aktualizacja zamiast trzech
2import { ref } from 'vue'
3
4function useBatchUpdate() {
5 const data = ref({ temp: 0, pressure: 0, humidity: 0 })
6
7 // ZLE: 3 oddzielne aktualizacje
8 function updateBad(temp, pressure, humidity) {
9 data.value.temp = temp // render
10 data.value.pressure = pressure // render
11 data.value.humidity = humidity // render
12 }
13
14 // DOBRZE: 1 aktualizacja (nowy obiekt)
15 function updateGood(temp, pressure, humidity) {
16 data.value = { temp, pressure, humidity } // 1 render
17 }
18
19 return { data, updateGood }
20}