Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. <Suspense> serverda
- 2.1 Sekin qismni o'rash
- 2.2 Natija oynasida: Suspense va use()
- 3. Oqim ichida nima bor?
- 3.1 Biz o'lchadik: bo'laklar va vaqt
- 3.2 Fallback, yashirin div va almashtirish
- 3.3 Biz topdik: JavaScript o'chiq bo'lsa
- 4. loading.tsx va <Suspense>
- 4.1 loading.tsx — butun sahifa uchun Suspense
- 4.2 Qaysi birini tanlash
- 4.3 Bir nechta Suspense chegarasi
- 5. Skeleton va layout siljishi
- 6. Promise'ni Client Component'ga uzatish
- 6.1 Naqsh
- 6.2 Qachon kerak
- 7. Oqimning cheklovlari
- 7.1 Status allaqachon ketgan
- 7.2 Oraliq qatlamlar
- 7.3 Xato oqim ichida
- 8. Portfolio qadami: oqim va commit
- 8.1 O'zgarish
- 8.2 Build va o'lchov
- 8.3 Halol narx
- 8.4 Commit
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): Suspense chegarasi qayerda?
- 2-mashq (o'rta): alohida Suspense chegaralari
- 3-mashq (qiyin): promise — client komponentga
- 4-mashq: Portfolio qadami
- 11. Real ishda
- Xulosa
- Manbalar
Next.js streaming va Suspense: sahifa bo'lak-bo'lak keladi
Qisqacha: Streaming (oqim) — server sahifani bir bo'lakda emas, tayyor bo'lgan qismlari bilan yuboradi. Sekin qismni
<Suspense fallback={…}>ga o'raysiz. Server avval qolgan sahifani va fallback'ni (masalan "yuklanmoqda…") yuboradi. Sekin qism tayyor bo'lganda uning HTML'i o'sha javobning davomida keladi va kichik skript uni joyiga qo'yadi.loading.tsx— butun sahifa atrofidagi avtomatik Suspense. Promise'niawaitsiz client komponentga uzatib, u yerdause()bilan o'qish ham mumkin. Narxi: oqim boshlangach status (200) o'zgarmaydi, va JavaScript o'chiq bo'lsa sekin qism ko'rinmaydi.
Bu darsda
- Sahifaning sekin qismini
<Suspense>bilan ajratasiz va qolgan qism kutmasligini haqiqiy o'lchov bilan ko'rasiz. - Server yuborgan HTML oqimini bo'lak-bo'lak o'qiysiz: fallback, yashirin
divva almashtirish skripti. loading.tsxbilan aniq<Suspense>farqini va qachon qaysi biri kerakligini tushuntirasiz.- Promise'ni Server Component'dan Client Component'ga uzatib,
use()bilan o'qiysiz. portfolioning «Men haqimda» sahifasiga GitHub bloki uchun oqim qo'shasiz va holatni commit qilasiz.
Oldin bilishingiz kerak: Serverda ma'lumot olish, Suspense, use(): promise va context'ni o'qish, Maxsus fayllar: loading, error, not-found, Rendering strategiyalari.
1. Nega bu kerak?
Oldingi darsda «Men haqimda» sahifasi GitHub profilini serverda oladigan bo'ldi. Lekin bitta muammo qoldi: sahifa GitHub javobini kutadi. GitHub 1 soniya o'ylasa, foydalanuvchi 1 soniya bo'sh ekranga qaraydi. Holbuki sarlavha, "Men Aziz, frontend dasturchiman" matni, bilimlar ro'yxati — hammasi tayyor, ular GitHub'ga bog'liq emas.
Choyxonani eslang. Siz osh, salat va choy buyurdingiz. Ofitsiant osh pishguncha hamma narsani ushlab turmaydi: avval choy va salatni olib keladi, osh esa tayyor bo'lganda keladi. Stolingiz bo'sh turmaydi.
Streaming — aynan shu. Server tayyor qismni darhol yuboradi, sekin qism uchun esa "joy band qiladi" (fallback) va keyin uni to'ldiradi.
18-qismda Suspense'ni brauzerda ishlatgansiz: lazy komponent yoki use(promise) kutayotganda fallback ko'rinardi. Next.js'da xuddi shu <Suspense> serverda ham ishlaydi — va bu yerda u HTML oqimini boshqaradi.
2. <Suspense> serverda
2.1 Sekin qismni o'rash
Kanon «Men haqimda» sahifasi:
import { Suspense } from "react";
import {
GithubBlok,
GithubBlokSkelet,
} from "./_komponentlar/GithubBlok";
export default function HaqimdaSahifasi() {
return (
<main id="asosiy">
<h1>Men haqimda</h1>
<p>Men Aziz, frontend dasturchiman. …</p>
{/* … bilimlar ro'yxati … */}
<h2>GitHub</h2>
<Suspense fallback={<GithubBlokSkelet />}>
<GithubBlok />
</Suspense>
<h2>O'qish rejam</h2>
{/* … jadval … */}
</main>
);
}GithubBlok — oldingi darsdagi async Server Component. U await connection() va await githubProfil(...) qiladi. <Suspense> React'ga aytadi: "bu qism kutayotganda o'rniga fallback ni ko'rsat, qolganini to'xtatma".
Fallback esa oddiy komponent:
export function GithubBlokSkelet() {
return <p aria-busy="true">GitHub ma'lumoti yuklanmoqda…</p>;
}aria-busy="true" — ekran o'quvchiga "bu joy hali yuklanmoqda" degan belgi (Malika uchun).
2.2 Natija oynasida: Suspense va use()
Natija oynasida server yo'q, lekin Suspense'ning xulqi aynan bir xil. Serverdagi so'rov o'rnida 800 ms dan keyin tugaydigan Promise'ni olamiz. Sahifa ochilganda fallback ko'rinadi, keyin profil:
import { Suspense, use } from "react";
type Profile = { login: string; repos: number };
// Serverdagi GitHub so'rovi o'rnida: 800 ms dan keyin javob
const profilePromise = new Promise<Profile>((resolve) =>
setTimeout(() => resolve({ login: "octocat", repos: 8 }), 800),
);
function GithubBlock({ profile }: { profile: Promise<Profile> }) {
const p = use(profile);
return (
<p>
@{p.login}: {p.repos} ta ochiq repo
</p>
);
}
function GithubSkeleton() {
return <p aria-busy="true">GitHub ma'lumoti yuklanmoqda…</p>;
}
export default function App() {
return (
<main>
<h1>Men haqimda</h1>
<h2>GitHub</h2>
<Suspense fallback={<GithubSkeleton />}>
<GithubBlock profile={profilePromise} />
</Suspense>
<h2>O'qish rejam</h2>
</main>
);
}Ekranda:
Men haqimda
GitHub
@octocat: 8 ta ochiq repo
O'qish rejamekran(...) qadami birinchi lahzani tekshiradi: "yuklanmoqda…" bor edi, va «O'qish rejam» sarlavhasi allaqachon ekranda turgan — u GitHub'ni kutmagan. 800 ms dan keyin fallback profil bilan almashdi.
Promise komponentdan tashqarida yaratilgan. Ichida yaratilsa, har render'da yangi Promise bo'lardi va use() cheksiz kutardi (use() darsidagi qoida). Next.js'da bu muammo yo'q: Server Component bir marta ishlaydi.
Tekshirib ko'ring:
<Suspense>ni olib tashlasak, natija oynasida nima bo'ladi? Haqiqiy Next.js sahifasida-chi?
Javob
Natija oynasida: eng yaqin Suspense chegarasi yo'q — butun ilova 800 ms "to'xtab" turadi, keyin hammasi birdan chiqadi. Next.js'da: sahifa GitHub javobini kutadi, birinchi bayt ham shu vaqtgacha kechikadi. Cache Components yoqilgan loyihada esa build uncached or runtime data xatosi bilan to'xtaydi — Next.js connection() li blokni Suspense'siz qoldirishga ruxsat bermaydi (Rendering strategiyalari darsidagi [stream]/[cache]/[block] tanlovi).
3. Oqim ichida nima bor?
3.1 Biz o'lchadik: bo'laklar va vaqt
Haqiqiy Next.js'da nima bo'lishini ko'rish uchun biz kanon holati 16 ni next build + next start bilan ishga tushirdik. GitHub o'rniga har javobni 800 ms kechiktiradigan lokal soxta server qo'ydik (GITHUB_API_URL), va /haqimda javobini kichik skript bilan bo'lak-bo'lak o'qidik. Qisqartirilgan chiqish:
status 200, sarlavhalar 42 ms
--- 1-bo'lak: 45 ms, 4146 belgi
<!DOCTYPE html><html lang="uz"><head><meta charSet="utf-8"/> …
--- 2-bo'lak: 47 ms, 9612 belgi
--- 3-bo'lak: 849 ms, 506 belgi
--- 4-bo'lak: 850 ms, 73 belgi
--- 5-bo'lak: 855 ms, 1184 belgi
<div hidden id="S:0"><p><a href="https://github.com/octocat">@ …
--- 6-bo'lak: 857 ms, 843 belgi
--- 7-bo'lak: 861 ms, 8453 belgi
--- 8-bo'lak: 864 ms, 14 belgi
</body></html>Ikki to'lqin ko'rinadi. Birinchisi — 45–47 ms: sahifaning deyarli hammasi (13 758 belgi). Ikkinchisi — 849–864 ms: GitHub bloki va oxiri. Ya'ni foydalanuvchi sarlavha, matn va jadvalni 800 ms oldin ko'radi.
Kanonning o'z o'lchovi (haqiqiy GitHub bilan, natijalar/16-oqim.txt) ham xuddi shu manzarani beradi:
- sarlavhalar21 ms
- 1-bo'lak (qobiq)55 ms
- GitHub bloki164 ms
- oxiri169 ms
Manba: IlmHamroh kanoni, portfolio v2 holat 16, next start, haqiqiy GitHub API, 2026-10-21
Ustunlarga qarang: qobiq 55 ms da keldi, GitHub bloki 164 ms da. Sovuq serverda (birinchi so'rov) raqamlar kattaroq — qobiq 354 ms, GitHub bloki 942 ms — lekin tartib o'sha. GitHub ishlamaganda (GITHUB_API_URL=http://127.0.0.1:9) zaxira matn 41 ms da keldi.
3.2 Fallback, yashirin div va almashtirish
1-bo'lakdagi GitHub joyi qanday ko'rinadi? Biz ishga tushirgan javobdan ko'chirma (asli bitta qator, telefonga sig'ishi uchun teglar orasidan bo'lindi):
<h2>GitHub</h2>
<!--$?-->
<template id="B:0"></template>
<p aria-busy="true">GitHub ma'lumoti yuklanmoqda…</p>
<!--/$-->
<h2>O'qish rejam</h2><!--$?--> va <!--/$--> — React'ning izohlari: "bu yerda kutilayotgan Suspense chegarasi bor". <template id="B:0"> — keyin topish uchun belgi. Ichida esa fallback — "yuklanmoqda…".
800 ms dan keyin, o'sha javobning davomida (5-bo'lak):
<div hidden id="S:0">
<p><a href="https://github.com/octocat">@<!-- -->octocat</a>:
<!-- --> <!-- -->8<!-- --> ta ochiq repo, <!-- -->20<!-- -->
ta kuzatuvchi.</p> …</div>
<script>$RB=[];$RV=function(a){ … };
$RC("B:0","S:0")</script>Tayyor HTML hidden atributli div ichida keladi — u ko'rinmaydi. Ortidan kichik inline skript: $RC("B:0","S:0") — "B:0 belgisidagi fallback'ni olib tashla, o'rniga S:0 dagi tarkibni qo'y". Brauzer bu skriptni darhol bajaradi — sahifaning katta JavaScript to'plami yuklanishini kutmasdan.
sequenceDiagram
participant B as Brauzer
participant S as Next.js server
participant G as GitHub
B->>S: GET /haqimda
S->>G: profil va repolar
S-->>B: 1-bo'lak: qobiq + fallback (45 ms)
Note over B: sarlavha, matn, "yuklanmoqda…"
G-->>S: javob (800 ms)
S-->>B: yashirin div S:0 + $RC skripti
Note over B: fallback o'rniga profilDiagrammadagi asosiy narsa: brauzer bitta so'rov yuboradi va bitta javob oladi. Javob shunchaki tez boshlanib, kech tugaydi. Qo'shimcha fetch ham, API ham yo'q.
Bo'laklar orasida self.__next_f.push(...) skriptlari ham bor — bu RSC payload (Server Components darsida ko'rgansiz): React keyinchalik sahifani interaktiv qilish (hydration) uchun ishlatadi.
Tekshirib ko'ring: HTML'ni o'qiydigan qidiruv roboti GitHub ma'lumotini ko'radimi?
Javob
Ha. Ma'lumot HTML javobining ichida bor — <div hidden id="S:0"> da. Bundan tashqari, Next.js hujjatiga ko'ra, faqat HTML o'qiydigan botlarni Next.js User-Agent bo'yicha taniydi va ular uchun metadata tayyor bo'lishini kutib yuboradi (htmlLimitedBots sozlamasi). Brauzer va JavaScript bajaradigan Googlebot esa oddiy oqimni oladi.
3.3 Biz topdik: JavaScript o'chiq bo'lsa
Almashtirish skript bilan bo'ladi. JavaScript o'chiq bo'lsa-chi? Bu kanon o'lchovi emas — o'zimiz ishga tushirgan tajriba. Biz o'sha serverda /haqimda ni headless Chrome'da ikki marta ochdik — JS yoqilgan va o'chiq holda — va «GitHub» bo'limidagi ko'rinadigan matnni oldik:
JS yoqilgan: "GitHub\n\n@octocat: 8 ta ochiq repo, 20 ta kuzatuvchi.\n\nvazifalar — ★ 3, oxirgi o'zgarish 2026-yil oktabr" | S:0 bor: false
JS o'chiq: "GitHub\n\nGitHub ma'lumoti yuklanmoqda…" | S:0 bor: true
chrome Chrome/155.0.8059.85JS o'chiq bo'lganda ma'lumot sahifada bor (S:0 bor: true), lekin hidden — foydalanuvchi abadiy "yuklanmoqda…" ni ko'radi. Bu streaming'ning halol narxi. Kanon loyihasida bu muammo keyingi holatda o'z-o'zidan yo'qoladi: Cache Components I darsida GitHub bloki keshlanib, sahifaning HTML'iga build vaqtida tushadi — oqim kerak bo'lmay qoladi.
Shu sababdan muhim kontentni (maqola matni, narx, asosiy CTA) Suspense ortiga yashirmang. Suspense — qo'shimcha, sekin, "bo'lmasa ham bo'ladi" qismlar uchun.
Yana bir bog'liqlik: $RC(...) — inline skript. Qat'iy CSP (script-src 'self') inline skriptni bloklaydi — u holda JS yoqilgan foydalanuvchi ham fallback'da qoladi. Kanon CSP'da 'unsafe-inline' ni qoldirgan: muqobili — nonce — butun saytni dinamik qilardi (next.config chuqur darsida o'lchov bilan).
4. loading.tsx va <Suspense>
4.1 loading.tsx — butun sahifa uchun Suspense
Maxsus fayllar darsida loading.tsx ni ko'rdingiz. Endi uning ichini bilasiz: Next.js page.tsx ni avtomatik <Suspense fallback={<Loading />}> ga o'raydi. Ya'ni:
// Next.js ichida taxminan shunday quradi:
<Layout>
<Suspense fallback={<Loading />}>
<Page />
</Suspense>
</Layout>Kanondagi namuna — loyihalar/[slug]/loading.tsx:
// loading.tsx — sahifa atrofida avtomatik <Suspense> (N-05).
// Faqat build vaqtida yaratilmagan slug'da ko'rinadi.
export default function LoyihaYuklanmoqda() {
return (
<main id="asosiy" aria-busy="true">
<h1>Loyiha yuklanmoqda…</h1>
</main>
);
}Kanon izohi telefonga sig'ishi uchun ikki qatorga bo'lindi.
4.2 Qaysi birini tanlash
loading.tsx |
<Suspense> |
|
|---|---|---|
| Nimani o'raydi | Butun sahifani | Siz tanlagan qismni |
| Fayl | Alohida fayl, papkada | JSX ichida |
| Navigatsiyada | Layout darhol, sahifa o'rnida loading | Sahifaning qolgani darhol |
| Qachon | Sahifa umuman ma'lumotsiz ma'nosiz | Sekin qism — qo'shimcha |
«Men haqimda» uchun loading.tsx yomon tanlov bo'lardi: GitHub'ni kutib, sarlavha, matn va jadvalni ham yashirardi. Shuning uchun kanon aniq <Suspense> ni GitHub blokining atrofiga qo'ydi. Next.js hujjati ham shuni tavsiya qiladi: Suspense'ni sekin ma'lumotga iloji boricha yaqin qo'ying.
Yana bir nozik joy: layout ichidagi sekin so'rov loading.tsx bilan yopilmaydi — loading.tsx layout'ning ichida turadi. Layout'dagi sekin qismga o'z <Suspense> i kerak.
4.3 Bir nechta Suspense chegarasi
Sahifada ikkita sekin qism bo'lsa — ikkita alohida <Suspense>. Ular mustaqil: qaysi biri oldin tayyor bo'lsa, o'sha oldin chiqadi. Bitta <Suspense> ichida ikkalasi bo'lsa — ikkalasi ham eng sekinini kutadi.
import { Suspense, use } from "react";
const later = <T,>(ms: number, value: T) =>
new Promise<T>((r) => setTimeout(() => r(value), ms));
const rate = later(300, "12 650 so'm");
const weather = later(800, "+18°, bulutli");
function Rate() {
return <p>Kurs: {use(rate)}</p>;
}
function Weather() {
return <p>Ob-havo: {use(weather)}</p>;
}
export default function App() {
return (
<main>
<h1>Toshkent bugun</h1>
<Suspense fallback={<p>Kurs: …</p>}>
<Rate />
</Suspense>
<Suspense fallback={<p>Ob-havo: …</p>}>
<Weather />
</Suspense>
</main>
);
}Ekranda:
Toshkent bugun
Kurs: 12 650 so'm
Ob-havo: +18°, bulutliHarakat qatoridagi ekran(...) qadamlari oraliq holatni tekshiradi: 400 ms da kurs allaqachon ko'rinadi, ob-havo esa hali "…". Ikkalasini bitta <Suspense> ga qo'ysangiz, kurs ham 800 ms kutardi. <T,> dagi vergul — .tsx faylda generic'ni JSX tegi deb o'ylamaslik uchun (React + TS IV).
Qanchalik mayda bo'lish kerak? Har kichik qismga alohida Suspense chegarasi — sahifa "popkorn" kabi sakrab chiqadi. Ma'noli guruhlar (yon panel, asosiy kontent, izohlar) — odatda yaxshiroq.
5. Skeleton va layout siljishi
Fallback — shunchaki "yuklanmoqda…" matni bo'lishi shart emas. Ko'p saytlar skeleton (kulrang qutilar) ko'rsatadi: kelajakdagi kontent shaklida bo'sh joy.
Yaxshi fallback'ning uchta belgisi:
- O'lchami tayyor kontentga yaqin. Fallback 1 qator, kontent 5 qator bo'lsa, ma'lumot kelganda pastdagi hamma narsa sakraydi — bu CLS (Cumulative Layout Shift — kutilmagan siljish, Core Web Vitals).
- Ekran o'quvchiga tushunarli.
aria-busy="true"yoki ko'rinmas "yuklanmoqda" matni. - Harakat ehtiyotkor. Miltillovchi animatsiya —
prefers-reduced-motionichida.
Kanondagi GithubBlokSkelet — bitta qator matn. GitHub bloki ham 1 qator matn + 3 qatorli ro'yxat. Siljish bor, lekin pastki qism (jadval) kichik. Kanon bu yerda soddalikni tanladi va CLS'ni Performance bosqichida butun sayt bo'yicha o'lchadi.
6. Promise'ni Client Component'ga uzatish
6.1 Naqsh
Ba'zan sekin ma'lumot interaktiv komponentga kerak bo'ladi: masalan, repolar ro'yxatini client tomonda saralash tugmasi bilan. Server Component Promise'ni await qilmasdan, uni prop sifatida beradi. Client Component esa use() bilan o'qiydi:
// app/haqimda/page.tsx — Server Component (sxema, kanonda yo'q)
import { Suspense } from "react";
import { RepoRoyxati } from "./RepoRoyxati";
export default function Sahifa() {
const repolar = repolarniOl("octocat"); // await YO'Q
return (
<Suspense fallback={<p>Repolar yuklanmoqda…</p>}>
<RepoRoyxati repolar={repolar} />
</Suspense>
);
}// RepoRoyxati.tsx — Client Component
"use client";
import { use, useState } from "react";
export function RepoRoyxati({
repolar,
}: {
repolar: Promise<{ nomi: string; yulduz: number }[]>;
}) {
const royxat = use(repolar);
const [tartib, setTartib] = useState<"nom" | "yulduz">("nom");
// … saralash tugmasi va ro'yxat
}Next.js Promise'ni brauzerga "uzatadi": server uni bajarib, natijani oqimda yuboradi, brauzerdagi use() esa shu natijani oladi. Promise natijasi seriyalanadigan bo'lishi shart (oddiy obyekt, massiv, son, satr, sana) — funksiya yoki klass nusxasi o'tmaydi (Client va server kompozitsiyasi).
6.2 Qachon kerak
| Variant | Qayerda kutadi | Qachon |
|---|---|---|
await Server Component'da |
Serverda | Ko'rsatish interaktiv emas (kanondagi GitHub bloki) |
Promise → client → use() |
Brauzer use() da |
Ma'lumot client komponentga kerak (filtr, saralash) |
| Client'da TanStack Query | Brauzer fetch |
Tez-tez yangilanadi, client'da qayta so'rash kerak (N-28) |
Kanon birinchi variantni tanladi: GitHub bloki faqat matn va havolalar, interaktiv emas. use() naqshi esa mashqda.
7. Oqimning cheklovlari
7.1 Status allaqachon ketgan
Oqim boshlanganda HTTP sarlavhalar va status kodi birinchi bo'lak bilan ketadi. Keyin ularni o'zgartirib bo'lmaydi. Next.js hujjati shunday deydi:
You cannot change the status code or headers after streaming starts.Tarjimasi: "Oqim boshlangach status kodi yoki sarlavhalarni o'zgartirib bo'lmaydi". Shuning uchun Suspense ichidagi notFound() sahifani 404 qila olmaydi — Next.js faqat <meta name="robots" content="noindex"> qo'shadi. Kanon buni Maxsus fayllar darsida o'z ko'zi bilan ko'rgan: noma'lum loyiha birinchi so'rovda 200 qaytargan. Yechim — 404'ni oqimdan oldin hal qilish (proxy.ts).
7.2 Oraliq qatlamlar
Server va brauzer orasidagi proksi (nginx) yoki CDN javobni to'plab, keyin bir martada yuborishi mumkin — shunda oqim foydasi yo'qoladi. Next.js hujjati nginx uchun X-Accel-Buffering: no sarlavhasini tavsiya qiladi. Deploy'da bu tekshiriladigan narsa (Deploy I).
7.3 Xato oqim ichida
GithubBlok xato tashlasa, xato eng yaqin error boundary ga — sahifaning error.tsx iga ketadi va butun sahifa o'rniga xato ekrani chiqadi. Kanon shuning uchun githubProfil ichida xatoni ushlab, null va zaxira matn qaytaradi (oldingi dars). Faqat GitHub joyini xato oynasi bilan almashtirish kerak bo'lsa — Suspense atrofiga alohida error boundary qo'yiladi (Error boundary).
8. Portfolio qadami: oqim va commit
Serverda ma'lumot olish darsida github.ts va GithubBlok ni yozdingiz. Bugun ularni sahifaga ulaymiz.
8.1 O'zgarish
src/app/(sayt)/haqimda/page.tsx ga:
import { Suspense } from "react";
import {
GithubBlok,
GithubBlokSkelet,
} from "./_komponentlar/GithubBlok";
// … <h2>GitHub</h2> ostiga:
<Suspense fallback={<GithubBlokSkelet />}>
<GithubBlok />
</Suspense>GithubBlokSkelet — GithubBlok.tsx ning o'zida, blok yonida.
8.2 Build va o'lchov
Kanon build jadvalidan /haqimda qatori (xom natijalar/tajriba/16-build-connection.txt):
├ ◐ /haqimda◐ — Partial Prerender: statik qobiq build vaqtida tayyor, GitHub bloki esa har so'rovda oqim bilan keladi (Rendering strategiyalari darsidagi belgi). Qolgan sahifalar o'zgarmadi.
Kanon o'lchovi: /haqimda TTFB 60 ms, brauzer JS 138.4 KB — o'zgarmadi, chunki GitHub bloki butunlay serverda. axe 0, hydration ogohlantirishi 0. Lint, tip, format — 0 xato.
8.3 Halol narx
Har sahifa ko'rish = GitHub'ga 2 ta so'rov. Kalitsiz limit — IP boshiga soatiga 60 ta. Demak soatiga taxminan 30 ta ko'rishdan keyin hamma tashrif buyuruvchi zaxira matnni ko'radi. Kichik portfolio uchun bu chegaraga yaqin. Yechim — natijani keshlash: Cache Components I darsida.
8.4 Commit
npm run lint, npm run tip, npm run build — toza bo'lsa:
git add -A
git commitCommit (kanon): Haqimda: GitHub profili serverda (kalitsiz, parallel, zaxira) va Suspense bilan oqim.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Butun sahifa sekin qismni kutadi | <Suspense> yo'q yoki juda yuqorida |
Chegarani sekin komponentga yaqin qo'ying |
loading.tsx bor, lekin sekin layout kutiladi |
loading.tsx layout ichida turadi |
Layout'dagi sekin qismga o'z <Suspense> i |
use() cheksiz "yuklanmoqda" (client'da) |
Promise render ichida yaratiladi | Promise'ni tashqarida yoki serverda yarating |
| Ma'lumot kelganda sahifa sakraydi | Fallback o'lchami kontentdan kichik | Skeleton'ni kontent shaklida qiling |
| Noma'lum sahifa 200 qaytaradi | notFound() oqim boshlangandan keyin |
Tekshiruvni Suspense'dan oldin yoki proxy'da |
| JS o'chiq — abadiy "yuklanmoqda" | Almashtirish inline skript bilan | Muhim kontentni Suspense ortiga qo'ymang |
| Qat'iy CSP bilan fallback almashmaydi | $RC inline skripti bloklangan |
'unsafe-inline' yoki nonce (N-38) |
| Deploy'da oqim yo'q, hammasi birdan | Proksi javobni to'playdi | X-Accel-Buffering: no |
10. Mashqlar
Mashqlar kurs/mashqlar/23/16-streaming/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): Suspense chegarasi qayerda?
Sahifada: sarlavha (tayyor), mahsulot tavsifi (tayyor), narx (baza, 50 ms), sharhlar (tashqi API, 1 200 ms), "o'xshash mahsulotlar" (tavsiya xizmati, 900 ms).
- Narx muhim va tez: Suspense
- Sharhlar sekin va qo'shimcha: o'z chegarasi
- O'xshash mahsulotlar sharhlar bilan bitta chegarada bo'lsa, ular ms da chiqadi
Yechim
- kerak emas — narx tez va muhim; uni Suspense ortiga yashirish JS'siz foydalanuvchidan narxni yashiradi.
- Suspense — sharhlar sekin, sahifa ularsiz ham foydali.
- 1200 — bitta Suspense chegarasi ichidagi hamma narsa eng sekinini kutadi. Alohida chegaralarda o'xshash mahsulotlar 900 ms da chiqardi.
2-mashq (o'rta): alohida Suspense chegaralari
«Bir nechta Suspense chegarasi» bo'limidagi misolni oling va unga uchinchi qism qo'shing: News — 500 ms dan keyin "Metro yangi bekat ochdi". Uchala qism alohida Suspense chegarasida bo'lsin. Har komponent chizilganda konsolga nomini yozsin (console.log("Kurs chizildi")) — qaysi tartibda chizilishini ko'ring.
Yechim
import { Suspense, use } from "react";
const later = <T,>(ms: number, value: T) =>
new Promise<T>((r) => setTimeout(() => r(value), ms));
const rate = later(300, "12 650 so'm");
const news = later(500, "Metro yangi bekat ochdi");
const weather = later(800, "+18°, bulutli");
function Rate() {
const value = use(rate);
console.log("Kurs chizildi");
return <p>Kurs: {value}</p>;
}
function News() {
const value = use(news);
console.log("Yangilik chizildi");
return <p>Yangilik: {value}</p>;
}
function Weather() {
const value = use(weather);
console.log("Ob-havo chizildi");
return <p>Ob-havo: {value}</p>;
}
export default function App() {
return (
<main>
<h1>Toshkent bugun</h1>
<Suspense fallback={<p>Kurs: …</p>}>
<Rate />
</Suspense>
<Suspense fallback={<p>Yangilik: …</p>}>
<News />
</Suspense>
<Suspense fallback={<p>Ob-havo: …</p>}>
<Weather />
</Suspense>
</main>
);
}Konsolda:
Kurs chizildi
Yangilik chizildi
Ob-havo chizildiEkranda:
Toshkent bugun
Kurs: 12 650 so'm
Yangilik: Metro yangi bekat ochdi
Ob-havo: +18°, bulutliChizilish tartibi — Promise'lar tugash tartibi (300, 500, 800 ms), JSX'dagi tartib emas. Ekrandagi joylashuv esa JSX tartibida qoladi: har fallback o'z joyini band qilgan.
3-mashq (qiyin): promise — client komponentga
ProjectList client komponentini yozing: u projects: Promise<{ name: string; stars: number }[]> prop'ini use() bilan o'qiydi va «Yulduz bo'yicha» tugmasi bosilganda ro'yxatni yulduzlar soni bo'yicha kamayish tartibida saralaydi (toSorted — Nusxa bilan o'zgartirish). Promise — 300 ms dan keyin uchta loyiha: vazifalar (3), bahor (7), landing (1). App uni <Suspense> ichida ko'rsatsin.
Yechim
import { Suspense, use, useState } from "react";
type Project = { name: string; stars: number };
// Server Component'dagi `repolarniOl()` o'rnida
const projectsPromise = new Promise<Project[]>((resolve) =>
setTimeout(
() =>
resolve([
{ name: "vazifalar", stars: 3 },
{ name: "bahor", stars: 7 },
{ name: "landing", stars: 1 },
]),
300,
),
);
function ProjectList({ projects }: { projects: Promise<Project[]> }) {
const list = use(projects);
const [byStars, setByStars] = useState(false);
const shown = byStars
? list.toSorted((a, b) => b.stars - a.stars)
: list;
return (
<>
<button onClick={() => setByStars(true)}>
Yulduz bo'yicha
</button>
<ul>
{shown.map((p) => (
<li key={p.name}>
{p.name} — {p.stars}
</li>
))}
</ul>
</>
);
}
export default function App() {
return (
<Suspense fallback={<p>Loyihalar yuklanmoqda…</p>}>
<ProjectList projects={projectsPromise} />
</Suspense>
);
}Ekranda:
Yulduz bo'yicha
bahor — 7
vazifalar — 3
landing — 1Next.js'da projectsPromise Server Component'da yaratiladi va prop bilan uzatiladi — ProjectList faylining boshida esa "use client" turadi. Saralash brauzerda bo'ladi, ma'lumot esa serverdan oqimda keladi.
4-mashq: Portfolio qadami
«Men haqimda» sahifasida GithubBlok ni <Suspense fallback={<GithubBlokSkelet />}> ga o'rang. npm run build jadvalida /haqimda yonida ◐ borligini tekshiring. Keyin next start bilan sahifani oching va DevTools → Network → haqimda so'rovining Timing yorlig'ida "Content Download" qancha davom etganini ko'ring. Toza bo'lsa — commit.
Yechim
Namuna — «Portfolio qadami» bo'limidagi kod. "Content Download" uzun bo'ladi (GitHub javobi vaqti): javob tez boshlanadi, lekin oqim GitHub kutilguncha ochiq turadi. Sinash uchun DevTools'da JavaScript'ni o'chirib (Command Menu → "Disable JavaScript") sahifani qayta yuklang — «Biz topdik: JavaScript o'chiq bo'lsa» bo'limidagi "yuklanmoqda…" holatini o'zingiz ko'rasiz. Commit xabari — kanondagi yoki o'zingizniki.
11. Real ishda
- Dashboard'lar — klassik streaming joyi: menyu va sarlavha darhol, grafiklar va jadvallar har biri o'z Suspense chegarasida.
- E-commerce — mahsulot nomi va narxi darhol (oqimsiz), sharhlar va tavsiyalar Suspense'da.
- AI javoblari — matn bo'lak-bo'lak keladigan chat interfeyslari ham oqimga tayanadi (Route Handlers darsida javob oqimi).
- Intervyuda: "
loading.tsxva<Suspense>farqi?", "Streaming'da nega 404 qaytarib bo'lmaydi?" — App Router bo'yicha tipik savollar.
Xulosa
- Streaming — bitta javob, bo'laklarda: qobiq darhol, sekin qism tayyor bo'lganda.
<Suspense fallback>sekin qismni ajratadi;loading.tsx— butun sahifa uchun avtomatik Suspense.- Oqimda tayyor HTML yashirin
divda keladi va inline skript uni fallback o'rniga qo'yadi — JS o'chiq bo'lsa ko'rinmaydi. - Mustaqil qismlar — alohida Suspense chegaralarida; bitta chegara eng sekinini kutadi.
- Promise'ni client komponentga uzatib
use()bilan o'qish mumkin — interaktiv ko'rinish uchun. - Oqim boshlangach status va sarlavhalar o'zgarmaydi; 404 ni oldinroq hal qiling.
Keyingi dars: generateStaticParams va segment konfiguratsiyasi — dinamik sahifalarni build vaqtida oldindan yaratamiz.
Manbalar
- Next.js hujjati — Streaming 16.4-bob: https://nextjs.org/docs/app/guides/streaming (2026-10-21 da o'qildi)
- Next.js hujjati — Fetching Data, Streaming: https://nextjs.org/docs/app/getting-started/fetching-data#streaming
- React hujjati —
<Suspense>: https://react.dev/reference/react/Suspense - React hujjati —
use: https://react.dev/reference/react/use - Kanon o'lchovlari:
portfoliov2, holat 16 (natijalar/16-oqim.txt,16-build-connection.txt); HTML bo'laklari va JS'siz tajriba — biz ishga tushirdik (next 16.4.0, Chrome 155, GitHub o'rnida 800 ms kechikadigan lokal soxta server).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!