Vue
November 24

Vue 3 da watch methodi

watch() metodi reaktiv ma'lumotlardagi o'zgarishlarni kuzatish va ularga javob qaytarish uchun ishlatiladi. Bu metod quyidagi asosiy xususiyatlarga ega:

Asosiy xususiyatlari

  1. Side Effects: Reactive data o'zgarganda side effect'larni bajarish imkonini beradi
  2. Async Operations: Async operatsiyalarni bajarish imkoniyati
  3. Control: Watching boshlanishi va to'xtatilishini nazorat qilish
  4. Deep watching: Obyektlarni chuqur kuzatish imkoniyati

Basic Usage

import { ref, watch } from 'vue'

const searchQuery = ref('')
const searchResults = ref([])

// Basic watcher
watch(searchQuery, async (newQuery, oldQuery) => {
  if (newQuery.trim()) {
    const results = await fetchSearchResults(newQuery)
    searchResults.value = results
  } else {
    searchResults.value = []
  }
})

Multiple Sources

Bir nechta source'larni kuzatish mumkin:

const firstName = ref('John')
const lastName = ref('Doe')

watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
  console.log(`Name changed from ${oldFirst} ${oldLast} to ${newFirst} ${newLast}`)
})

Deep Watching

Obyekt ichidagi o'zgarishlarni ham kuzatish:

const user = reactive({
  name: 'John',
  profile: {
    age: 25,
    city: 'Tashkent'
  }
})

// Deep watcher
watch(user, (newValue, oldValue) => {
  console.log('User data changed:', newValue)
}, { deep: true })

Immediate Watching

Watcher ni darhol ishga tushirish:

const todoId = ref(1)
const todoData = ref(null)

watch(todoId, async (newId) => {
  todoData.value = await fetchTodo(newId)
}, { immediate: true })

Watch vs watchEffect

Vue 3 da ikkita watching mexanizmi mavjud:

watch

  • Aniq source va callback bilan ishlaydi
  • Old va new qiymatlarni taqdim etadi
  • Watching qachon ishga tushishini nazorat qilish mumkin
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
  console.log(`Counter changed from ${oldValue} to ${newValue}`)
})

watchEffect

  • Avtomatik ravishda dependency'larni kuzatadi
  • Callback ichida ishlatilgan har qanday reactive value o'zgarganda ishga tushadi
  • Darhol ishga tushadi
const todos = ref([])
const error = ref(null)

watchEffect(async () => {
  try {
    todos.value = await fetchTodos()
  } catch (e) {
    error.value = e
  }
})

Stop Watching

Watcher'ni to'xtatish:

const stop = watch(source, callback)
// Kerak bo'lganda
stop()

Best Practices

  1. Cleanup function ishlatish
watch(id, async (newId, oldId, onCleanup) => {
  const { response, cancel } = doAsyncWork(newId)
  
  // Agar yangi watcher ishga tushsa, oldingi requestni bekor qilish
  onCleanup(() => cancel())
  
  data.value = await response
})
  1. Error Handling
watch(source, async (value) => {
  try {
    await someAsyncOperation()
  } catch (error) {
    console.error('Error in watcher:', error)
  }
})
  1. Debouncing
import { debounce } from 'lodash'

const debouncedWatch = watch(searchInput, debounce((value) => {
  // Search logic here
}, 300))


Anti-patterns va Ogohlantirishlar

1. Computed property o'rniga watch ishlatish

// ❌ Yomon
watch([firstName, lastName], ([first, last]) => {
  fullName.value = `${first} ${last}`
})

// βœ… Yaxshi
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

Nega bu anti-pattern?

  • Performance: Watch har doim callback funksiyani to'liq bajaradi, computed esa keshlanadi va faqat dependency o'zgargandagina qayta hisoblanadi
  • Code readability: Computed aniq ko'rsatadiki bu yangi value hisoblanmoqda. Watch esa side-effect uchun mo'ljallangan
  • Debugging: Computed bilan value qayerdan kelayotganini kuzatish osonroq
  • Testing: Computed propertylarni test qilish osonroq chunki ular pure function

2. Bir watcher ichida ko'p vazifalarni bajarish

// ❌ Yomon
watch(user, () => {
  updateProfile()
  sendAnalytics()
  refreshUI()
  loadRelatedData()
})

// βœ… Yaxshi - Alohida watcherlar
watch(user, updateProfile)
watch(user, sendAnalytics)
watch(user, refreshUI)
watch(user, loadRelatedData)

Nega bu anti-pattern?

  • Maintainability: Har bir watcher alohida mas'uliyatga ega bo'lishi kerak (Single Responsibility Principle)
  • Error handling: Bitta funksiyada xato chiqsa, boshqa funksiyalar ham ishlamay qoladi
  • Testing: Ko'p vazifali watcherni test qilish murakkab
  • Debug qilish: Xato qaysi operatsiyada yuz berganini aniqlash qiyin

3. Watch ichida reactive data o'zgartirish

// ❌ Yomon
watch(count, (newCount) => {
  anotherCount.value = newCount * 2
})

// βœ… Yaxshi
const anotherCount = computed(() => count.value * 2)

Nega bu anti-pattern?

  • Infinite loop xavfi: Agar watched value watcherni ichida o'zgartirilsa, infinite loop yuzaga kelishi mumkin
  • State management murakkabligi: Reactive datani bir necha joyda o'zgartirish debug qilishni qiyinlashtiradi
  • Vue'ning reaktivlik tizimidan noto'g'ri foydalanish: Bu vazifa computed property uchun mo'ljallangan

4. Watcherda og'ir operatsiyalarni to'g'ridan-to'g'ri bajarish

// ❌ Yomon
watch(searchQuery, (query) => {
  const results = heavyComputation(query)
  searchResults.value = results
})

// βœ… Yaxshi
watch(searchQuery, debounce(async (query) => {
  const results = await heavyComputation(query)
  searchResults.value = results
}, 300))

Nega bu anti-pattern?

  • Performance: Har bir o'zgarishda og'ir hisob-kitoblar UI ni sekinlashtiradi
  • User Experience: Foydalanuvchi kiritayotgan vaqtda ham hisob-kitoblar amalga oshiriladi
  • Resource usage: Keraksiz hisob-kitoblar tizim resurslarini behuda sarflaydi

5. Deep watching ni keraksiz ishlatish

// ❌ Yomon
watch(complexObject, (newVal) => {
  // Something with newVal
}, { deep: true })

// βœ… Yaxshi - Faqat kerakli propertyni kuzatish
watch(() => complexObject.specificProperty, (newVal) => {
  // Something with newVal
})

Nega bu anti-pattern?

  • Performance: Deep watching katta obyektlar uchun sezilarli performance yo'qotishga olib keladi
  • Unnecessary updates: Keraksiz propertylar o'zgarganda ham watcher ishga tushadi
  • Memory usage: Vue har bir property uchun reactive wrapper yaratishi kerak bo'ladi

6. watchEffect ichida async operatsiyalarni noto'g'ri boshqarish

// ❌ Yomon
watchEffect(async () => {
  const data = await fetchData()
  results.value = data
})

// βœ… Yaxshi
watchEffect((onCleanup) => {
  const controller = new AbortController()
  onCleanup(() => controller.abort())
  
  fetchData({ signal: controller.signal })
    .then(data => {
      results.value = data
    })
})

Nega bu anti-pattern?

  • Race conditions: Oldingi request tugatilmasdan yangi request boshlanishi mumkin
  • Memory leaks: Cleanup funksiyasisiz eskii requestlar to'planib qolishi mumkin
  • State inconsistency: Natijalar noto'g'ri tartibda kelishi mumkin

Best Practice Recommendations

  1. Watch o'rniga computed ishlatish mumkin bo'lgan holatlarni aniqlang
    • Agar yangi value hisoblash kerak bo'lsa - computed ishlating
    • Agar side-effect (API call, DOM manipulation) kerak bo'lsa - watch ishlating
  2. Watcherlarni kichik va aniq vazifalar uchun ajrating
    • Har bir watcher bitta vazifani bajarsin
    • Vazifalar bir-biriga bog'liq bo'lsa, alohida composable funksiya yarating
  3. Async operatsiyalar uchun cleanup ishlatishni unutmang
    • Race conditionlardan qochish uchun
    • Memory leaklarni oldini olish uchun
    • Network requestlarni to'g'ri boshqarish uchun
  4. Watch va watchEffect ni to'g'ri tanlang
    • Watch - aniq dependency va old/new value kerak bo'lganda
    • watchEffect - dependency avtomatik aniqlanishi kerak bo'lganda
  5. Performance optimizatsiyasi
    • Deep watching o'rniga specific propertylarni kuzating
    • Debounce/throttle qo'llang
    • Og'ir operatsiyalarni optimize qiling

Hozircha shu, keyingi postlarda ko'rishguncha. Stay tuned...
@peaceofcode