Yangilanishlarni amalga oshirish

RSC’larning ko’plab ijobiy jihatlari bo’lsa-da, e’tibor berish kerak bo’lgan ayrim cheklovlar ham mavjud, ikki xil turdagi komponentlar (server va klient) haqida o’ylash kerak bo’lgan ortiqcha aqliy o’ylashni qabul qilish. Buning sababi shundaki, barcha komponentlar server komponentlari bo’la olmaydi.

Misol: Counter komponenti

Masalan, foydalanuvchi + tugmasini bosganida hisob qiymatini 1 ga oshiradigan oddiy hisoblagich komponentini ko’rib chiqaylik:

function Counter() {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <h1>Hello friends, look at my nice counter!</h1>
      <p>About me: I like pie! Sign my guest book!</p>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

Bu komponent ikkita sababga ko’ra server komponenti bo’lishi mumkin emas:

useState’dan foydalanish

Bu useState’dan foydalanadi va bu faqat klient tomonda ishlatiladigan API hisoblanadi. Bu shuni anglatadiki, server countning boshlang’ich qiymatini bilmaydi, shuning uchun u boshlang’ich HTML’ni render qila olmaydi. Bu muammo, chunki server dastlabki HTML’ni render qilishi kerak, keyin klient interaktiv UI’ni render qilishi mumkin.

Server muhitida “state” tushunchasi bir nechta klientlar o’rtasida ulashiladi. Biroq, RSC’lar joriy qilinishidan oldin, React’da state hozirgi dasturga lokalizatsiya qilingan edi. Ushbu farq xavf tug’dirishi mumkin. Bu bir nechta klientlar o’rtasida state’ning sizib chiqishiga olib kelishi, potensial ravishda nozik ma’lumotlarni oshkor bo’lishi muammosiga sabab bo’lishi mumkin. Ushbu farq va unga tegishli xavfsizlik xavflari sababli, RSC’lar server tomonda useStatedan foydalanishni qo’llab-quvvatlamaydi. Chunki server tomonidagi state klient tomonidagi state’dan tubdan farq qiladi.

Bundan tashqari, useStatedan olinadigan setState dispetcher funksiyasi seriyalashtirilishi va klientga yuborilishi kerak, ammo funksiyalar seriyalashtirilmaydi, shuning uchun bu imkonsiz bo’ladi.

onClick’dan foydalanish

onClick ham faqat klient tomonida ishlatiladigan API hisoblanadi. Chunki serverlar interaktiv emas: serverda ishlayotgan jarayonni “click” qilib bo’lmaydi, shuning uchun server komponentlarida onClick biroz imkonsiz holatdir. Bundan tashqari, server komponentlari uchun barcha props’lar seriyalashtirilishi kerak, chunki server props’larni seriyalashtirishi va klientga yuborishi kerak, funksiyalar esa seriyalashtirilmaydi.

Klient va server komponentga ajratish

Shu sababli, agar biz server komponentlarining kuchidan foydalanishni xohlasak, oddiy hisoblagich endi server qismi va klient qismiga bo’linishi kerak:

// Server Component
function ServerCounter() {
  return (
    <div>
      <h1>Hello friends, look at my nice counter!</h1>
      <p>
        About me: I like to count things and I'm a counter and sometimes I count
        things but other times I enjoy playing the Cello and one time at band
        camp I counted to 1000 and a pirate appeared
      </p>
      <InteractiveClientPart />
    </div>
  );
}
 
// Client Component
"use client";
function InteractiveClientPart() {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

Bu misol biroz tasavvuriy misol bo’lsa-da, lekin u shunchaki React komponentini olib, uni server komponentiga aylantira olmasligingizni ko’rsatadi.. Sizning komponentingizning qaysi qismlari serverda render qilinishi va qaysi qismlari klientda render qilinishi to’g’risida o’ylashingiz kerak. Bu bir oz qiyinchilik keltirib chiqaradi, chunki bu misolda serverda render qilinadigan va klientda render qilinadigan qismlar aniq, lekin real hayotdagi dasturlarda bunchalik aniq bo’lmasligi mumkin.

Shunday qilib, biz hisoblagich dasturimizning interaktiv qismidan kichik bir qismini ajratib oldik va faqat shu qism bizning foydalanuvchilarimizga JavaScript to’plam paketi sifatida yetkaziladi; qolgan qismi esa yetkazilmaydi. Natijada, biz tarmoq orqali juda kichik JavaScript paketlarini jo’natamiz, bu esa yuklash vaqtlarini tezlashtiradi va foydalanuvchilarimiz uchun CPU va tarmoq jihatidan yanada yaxshi ishlashni ta’minlaydi, chunki JavaScript’ni tahlil qilish va bajarish uchun kamroq ish qilinishi kerak va yuklanadigan ma’lumotlar kam bo’ladi.

Shu sababli, biz xavfsiz serverda imkon qadar ko’proq render qilishni xohlaymiz, shunda klient tomondagi paketlar ichiga kod qo’shmaymiz.

Ichki ishlash mexanizmi

Qo’shimcha aqliy o’ylashni hisobga olgan holda, keling, React qanday qilib server komponentlari va klient komponentlarini ichki mexanizmda alohida ajratib va ulardan qanday foydalanishini ko’rib chiqaylik. Bu muhim, chunki bu bizga dasturimizga yangilanishlar kiritishni tushunishga yordam beradi.

Klient komponentlari va grafiklar

Klient komponentlari "use client" direktivasi yordamida ajratiladi va bu direktiva klient komponentlarini o’z ichiga olgan faylning yuqori qismida joylashadi. RSCs server va klient komponentlarini ushbu direktivalarning ishlatilishiga asoslanib farqlash uchun keyingi avlod vositalarini talab qiladi.

Keyingi avlod vositalarini to’plash vositasi(bundler) yoki to’plash vositasi konfiguratsiyasi(bundler configuration)dan foydalangan holda, to’plash vositalari React ilovalari uchun alohida modul grafiklarini ishlab chiqarish imkoniyatiga ega: server grafik va klient grafik. Server grafiki hech qachon to’plam paketiga aylantirilmaydi, chunki u foydalanuvchilarga yetkazib berilmaydi, lekin "use client" direktivasi bilan boshlanuvchi barcha fayllar bitta klient to’plam paketi yoki bir nechta to’plam paketlarga qo’shiladi, ularni lazy loading orqali yuklab olish mumkin. Ushbu implementatsiya tafsilotlari RSCs ustida qurilgan freymvorklar bilan bog’liq.

Shunday qilib, kontseptual jihatdan bizda serverda bajariladigan server grafigi va klientda kerak bo’lganda yuklab olinadigan va bajariladigan bitta yoki bir nechta klient to’plam paketlari mavjud. Ammo React klient komponentlarini qachon import qilish va bajarish kerakligini qanday biladi? Buni tushunish uchun biz odatiy React daraxtini ko’rib chiqishimiz kerak. Keling, hisoblagich misolimizdan foydalanamiz.

Komponentlar daraxti

Pastdagi rasmda, hisoblagich ilovamiz uchun komponentlar daraxtini vizualizatsiya qilamiz, bu yerda to’rtta komponentlar serverda render qilingan va yashil komponentlar klientda render qilingan. Daraxtning ildizi server komponenti bo’lgani uchun, butun daraxt serverda render qilinadi. Biroq, InteractiveClientPart komponenti klient komponenti bo’lgani uchun, u serverda render qilinmaydi. Buning o’rniga, server klient komponenti uchun placeholder, ya’ni joy saqlab turuvchi, render qiladi, bu esa klient to’plash vositasi ishlab chiqargan maxsus modulga havola hisoblanadi.

Ushbu modul havolasi asosan “agar siz daraxtda ushbu nuqtaga yetib kelsangiz, ushbu maxsus moduldan foydalanish vaqti keldi” degan ma’noni anglatadi. Server va klient komponentlarini ko'rsatuvchi daraxt

Modul har doim faqat kerak bo’lganda yuklab olinishi shart emas, balki dastlabki to’plam to’plam paketdan ham yuklab olinishi mumkin, chunki to’plash vositalari foydalanuvchilarga yetkazib beradigan to’plam paketlarda ko’plab modullarni qo’shadi. Bu tom ma’noda, getModuleFromBundleAtPosition([0,4]) yoki shunga o’xshash narsa bo’lishi mumkin. Asosiy nuqtasi shundaki, server to’g’ri klient moduliga havolani yuboradi va React klient tomonida bo’sh joyni to’ldiradi.

Bu jarayon sodir bo’lganda, React modul havolasini klient to’plam paketi ichidagi haqiqiy modul bilan almashtiradi. Bu biroz soddalashtirish bo’lsa-da, mexanizmni yaxshi tushunishimizga yordam beradi. Klient komponenti klientda render qilinadi va klient komponenti bilan odatdagidek interaktivlikka kirishish mumkin bo’ladi. RSCs uchun keyingi avlod to’plash vositalari kerak bo’lishining sababi shundaki, ular server va klient komponentlari uchun alohida modul grafiklarini ishlab chiqarishi kerak.

Render qilingan daraxt

Amalda, bu bizning hisoblagich misolimizda server quyidagi daraxtni render qiladi:

{
  $$typeof: Symbol(react.element),
  type: "div",
  props: {
    children: [
      {
        $$typeof: Symbol(react.element),
        type: "h1",
        props: {
          children: "Hello friends, look at my nice counter!"
        }
      },
      {
        $$typeof: Symbol(react.element),
        type: "p",
        props: {
          children: "About me: I like to count things"
        }
      },
      // ClientPart elementi uchun vaqtinchalik joy tutuvchi(placeholder) modul havolasi
      // Diqqat qiling: bu modul havolasi!
      {
        $$typeof: Symbol(react.element),
        type: {
          $$typeof: Symbol(react.module.reference),
          name: "default",
          filename: "./src/ClientPart.js",
          moduleId: "client-part-1234"
        },
        props: {
          children: [
            {
              // ...boshqa server komponentlari va klient modul havolalari
              $$typeof: Symbol(react.element),
              type: {
                $$typeof: Symbol(react.module.reference),
                name: "default",
                filename: "./src/AnotherClientComponent.js"
              },
              props: {
                children: [],
              }
            },
            {
              $$typeof: Symbol(react.element),
              type: "div",
              props: {
                children: "I am a server component"
              }
            }
          ]
        }
      }
    ]
  }
}

Ushbu daraxt klient tomoniga yuboriladi va React uni render qilar ekan, modul havolasiga duch kelganda, React ushbu modul havolasini klient to’plam paketi ichidagi haqiqiy modul bilan “aqlli” tarzda almashtiradi. Shunday qilib, React qachon klient komponentlarini import qilish va bajarish kerakligini biladi.

Klient komponentlarining qayta qo’llanilishi

Shunday qilib, biz to’plash vositasi(bundler) serverda butun daraxtni render qilish imkoniyatiga ega ekanligini va faqat klientda to’ldirilishi kerak bo’lgan “teshiklar” qoldirilishini ko’ramiz, shu bilan birga, serverda klient komponentlarining bolalarini rekursiv tarzda render qilib, to’liq daraxt hosil qiladi. Keyin esa, klient zarur bo’lgan har qanday teshikni klient to’plam paketlarini yuklab olish va bajarish orqali to’ldiradi.

Server komponentlarini, shuningdek, Suspense hududlariga o’rash ham mumkin, bunda freymvorklar “tayyor” bo’lganda ularni server tomonidan foydalanuvchilarga stream tarzida uzatish uchun zarur ishlarni bajaradi: ya’ni ular talab qiladigan har qanday ma’lumotlar olinadi va boshqa barcha operatsiyalar asinxron tarzda bajariladi.

Umid qilamizki, endi biz klient komponentlarining server komponentlaridan qanday ajratilishini tushundik, bu esa RSC’larga yo’naltirilgan ilovalarda yangilanishlarni amalga oshirish imkonini beradi. "use client" bilan belgilangan klient komponentlari ichki state’lar(useState kabi) va onClick kabi event handler’larni muammosiz o’z ichiga olishi mumkin.

Endi biz klient komponentlari bilan siklni yopganimizni va server komponentlari qanday ishlashini, shuningdek, klient komponentlari qanday qilib klient to’plam paketlariga qo’shilishini tushunganimizdan so’ng, bu mavzular atrofida bir oz nozik jihat(nyuans)larni muhokama qilishimiz kerak.