July 12

Reaktivlik asoslari                        (Reactivity Fundamentals)

Vue dokumentatsiyasini o'qishda davom etamiz. Reaktivlik asoslari qismiga keldik.

Vue'da asosan, juda ko'p hollarda reactive state hosil qilish uchun ref funksiyasidan foydalaniladi:

import { ref } from 'vue'

const count = ref(0)

ref funksiyasi argumentga berilgan qiymatni obyekt ichiga olib o'sha obyektni qaytaradi. Bu obyektda value deb nomlangan maydon bo'lib, bu maydonda biz argumentda bergan qiymat saqlanadi. Shu orqali biz qiymatni o'qishimiz yoki o'zgartirishimiz mumkin.

const count = ref(0)
console.log(count) // { value: 0 } 
console.log(count.value) // 0  
count.value++ 
console.log(count.value) // 1

Template qismi

Component'ning template qismida refni .value yozmasdan ishlatishimiz mumkin:

<div>{{ count }}</div>

Template'da qiymatni o'qiganimiz kabi o'zgartirishda ham .value yozishimiz shart emas:

<button @click="count++">  {{ count }} </button>

Ref o'zi qanday ishlashini esa menimcha alohida post orqali o'rganganimiz maqul. Chuqurroq Evan you aka bilan birga ushbu videoda sho'ng'ishingiz mumkin.

Chuqur reaktivlik (Deep Reactivity)

ref funksiyaga o'ziga berilgan qiymatni chuqur reaktivlashtirib beradi, ya'ni ichma-ich obyektlar yoki massivlarning har qanday qismida o'zgarish bo'lganini aniqlay oladi. Bu narsa Vue 2 da muammoli edi va ichma-ich obyekt va massivlarni o'zgartirish uchun alohida usuldan foydalanishga to'g'ri kelardi. Vue 3 da esa bu muammolar proxy'lar hisobiga endi yo'q

import { ref } from 'vue'  

const obj = ref({  
  nested: { count: 0 },  
  arr: ['foo', 'bar'] 
})  

function mutateDeeply() {  
  obj.value.nested.count++  
  obj.value.arr.push('baz') 
}

Chuqur reaktivlik kerak bo'lmagan holatlarda shallowReffunksiyasini ishlatishimiz mumkin. Juda katta obyektlarda yoki ichida component'lar bo'lgan obyekt yoki massivlarda shallowRef ishlatish orqali performance'ni yaxshilashimiz mumkin.

DOM yangilanish vaqti (DOM Update Timing)

Reaktiv qiymatni o'zgartirganimizda u darhol o'zgaradi. Ya'ni:

<script setup>
const count = ref(0);
count.value++;

console.log(count.value) // console'ga 1 chiqadi
</script>

Lekin DOM'da o'zgarishlar darhol qilinmaydi, ya'ni component'ning template qismida count o'zgaruvchisini ishlatgan bo'lsak u darhol yangilanib qolmaydi. Vue barcha o'zgarishlarni bittada keyingi sikl (next tick)da amalga oshiradi. Buning sababi Vue bir nechta reaktiv qiymatlar o'zgarganda har biri uchun alohida DOM'ni yangilamasdan hammasini bittada oxirida qiladi.

Keling shuni real misol bilan ko'ramiz. Shartlar quyidagicha bo'lsin:

  1. Checkbox bor
  2. Checkbox check qilinganda input ko'rinsin va focus bo'lsin
<script setup>
import { ref } from "vue";

const inputEl = ref();
const isChecked = ref(false);

const onChange = (event) => {
  isChecked.value = event.target.checked;
  if(isChecked.value) {
    input.value.focus();
  }
}
</script
<template>
  <div>
    <input type="checkbox" />
    <input v-if="isChecked" ref="inputEl" @change="onChange" />
  </div>
</template>

Yuqoridagi kodda quyidagicha xatolik sodir bo'ladi:

Xatolik

Sababi biz isCheckedning qiymatini o'zgartirganimizdan so'ng HTML darhol yangilanmaydi. Buni to'g'irlash uchun nextTick funksiyasini ishlatishimiz mumkin:

Kodni buyerda ishlatib ko'rishingiz mumkin

<script setup>
import { ref, nextTick } from "vue";

const inputEl = ref();
const isChecked = ref(false);

const onChange = async (event) => {
  isChecked.value = event.target.checked;
  if(isChecked.value) {
    await nextTick();
    input.value.focus();
  }
}
</script

reactive()

ref dan tashqari reactive orqali ham reactive state hosil qilishimiz mumkin.

ref funksiyasi berilgan argumentni reaktiv obyekt ichiga olib qaytarsa, reactive esa o'ziga berilgan obyektning o'zini reaktiv qilib qaytaradi. Shuning uchun reactive funksiyasiga obyekt berishimiz kerak. Primitive tipdagi qiymatlarni bera olmaymiz.

import { reactive } from 'vue'  

const state = reactive({ count: 0 })

Template qismida ishlatilishi:

<button @click="state.count++">  
  {{ state.count }} 
</button>

reactive funksiyasi bizga Javascript Proxy obyekt qaytaradi va bu odatiy obyekt kabi ishlaydi. Proxy qilishdan maqsad Vue proxy orqali obyektdagi o'zgarishlarni aniqlay oladi va shunga qarab amallarni bajarishi mumkin bo'ladi (watcherlarni ishga tushirish, UI'ni yangilash va boshqalar)

reactive ham xuddi ref kabi ichma-ich obyektlarni ham reactive ichiga olib chiqadi. Agar faqat eng tepadagi obyektni reactive qilmoqchi bo'lsak shallowReactive funksiyasini ishlatishimiz mumkin:

import { shallowReactive } from 'vue';

const user = shallowReactive({
  name: 'Ali',
  details: {
    age: 24,
    phone: '+998 93 102 01 01'
  }
});

watch(user, () => {
  console.log('User changed');
});
<template>
  <input v-model="user.details.phone" />
</template>

Yuqoridagi kodda input'ga biror qiymat yozganimiz bilan watch ichiga yozilgan kod ishlamaydi. Sababi biz shallowReactive ishlatganmiz.

Reactive Proxy vs. Original

reactive tomonidan qaytarilgan proxy obyekt biz bergan original obyektga teng bo'lmaydi. Shu tomoniga e'tiborli bo'lishimiz lozim.

const raw = {}
const proxy = reactive(raw)

console.log(proxy === raw) // false


reactive() ning cheklovlari

  1. Faqat obyektlar bilan ishlaydi (object, array, Map, Set kabilar). string, number va boolean kabi primitive tipdagi qiymatlarni qabul qila olmaydi.
  2. Obyektni to'liq qayta o'zlashtirib (almashtirib bo'lmaydi). reactive o'ziga berilgan obyektni proxy qilganligi uchun uni boshqa obyekt bilan almashtirib bo'lmaydi, bunday holatda reaktivlik yo'qoladi. Faqat ichidagi maydonlarini o'zgartirishimiz mumkin bo'ladi.
  3. Destrukturizatsiya qilish muammolari
const state = reactive({ count: 0 })  
// count o'zgaruvchisi tepadagi obyektdan uzilib qoladi. 
// count'ga qilingan o'zgarishlar aniqlanmaydi 
let { count } = state 
// Quyidagi funksiya ham state'dagi o'zgarishlarni sezmaydi. 
// Sababi funksiyaga oddiy qiymat ketyapti, reactive state emas.
// Buning uchun esa state obyektini to'liq yuborishimiz kerak funksiyaga
callSomeFunction(state.count)

Hozircha shular, batafsil dokumentatsiyada o'qib chiqishingiz mumkin. Ayniqsa ref unwrapping haqida ham o'qib qo'yish kerak.

E'tiboringiz uchun rahmat!