Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. Sukut bo'yicha — server
- 2.1 Siz allaqachon yozgansiz
- 2.2 Ikki xil chiqish
- 3. async komponent: faylni to'g'ridan o'qish
- 3.1 Kanondagi blog
- 3.2 blog.ts — fayl tizimi komponent yonida
- 3.3 Natija oynasida: komponent — oddiy async funksiya
- 4. Sirlar serverda qoladi
- 4.1 Biz tekshirdik
- 4.2 Sir va dangerouslySetInnerHTML
- 5. Server Component'ning cheklovlari
- 5.1 Nima yo'q
- 5.2 Biz sinadik: haqiqiy xatolar
- 6. RSC payload: brauzerga nima boradi?
- 6.1 Kanon o'lchovi
- 6.2 Payload ichida
- 6.3 Raqamlar: brauzer JS
- 7. Biz topdik: fayl o'qish ham "keshlanmagan ma'lumot"
- 8. Portfolio qadami: blog (1-qism)
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): server yoki client?
- 2-mashq (o'rta): frontmatter tahlili
- 3-mashq (qiyin): payload modelini yasang
- 4-mashq: Portfolio qadami
- 11. Real ishda
- Xulosa
- Manbalar
React Server Components Next.js'da: serverda ishlaydigan komponentlar va RSC payload
Qisqacha: Next.js App Router'da har komponent sukut bo'yicha Server Component — u faqat serverda (yoki build vaqtida) ishlaydi va brauzerga o'z JS kodini yubormaydi. U
asyncbo'la oladi: faylni o'qiydi, bazaga yoki API'ga so'rov yuboradi,process.envdagi sirni ishlatadi — va bularning hech biri brauzerga chiqmaydi. Evaziga undauseState,useEffect,onClickyo'q. Brauzerga natija ikki ko'rinishda boradi: tayyor HTML va RSC payload — komponent daraxtining matnli tasviri.
Bu darsda
- Server Component qayerda va qachon ishlashini tushuntirasiz.
asynckomponentda faylni o'qiysiz —portfolioning Markdown blogi.- Sirli kalit brauzerga chiqmasligini o'zingiz tekshira olasiz.
- Server Component'da nima mumkin emasligini haqiqiy build xatolari bilan bilasiz.
- RSC payload'ni o'qiy olasiz va
markedkutubxonasi brauzerga 0 bayt qo'shganini ko'rasiz.
Oldin bilishingiz kerak: Parallel va intercepting routes, Fayl-asosli routing, Next.js nima, async / await.
1. Nega bu kerak?
Endi portfolio ga blog qo'shamiz. Postlar Markdown fayllarda (src/blog/*.md), ularni HTML'ga aylantirish uchun kutubxona kerak — kanon marked ni tanladi.
vazifalar (Vite SPA) da bu qanday bo'lardi? Kutubxona npm i qilinadi, import { marked } from "marked" yoziladi — va u brauzer to'plamiga (bundle) tushadi. Har foydalanuvchi uni yuklaydi, garchi postlar hech qachon o'zgarmasa ham. Markdown fayllarning o'zi ham brauzerga borishi kerak — fetch bilan yoki to'plam ichida.
Biz o'lchadik: marked ni brauzerda ishlaydigan komponentga qo'yganimizda u alohida JS bo'lagi bo'lib keldi — 13.3 KB gzip (45.7 KB xom). Bitta post sahifasi uchun.
Kanon esa xuddi shu blogni Server Component bilan qurdi. Natija: blog sahifasi boshqa sahifalardan atigi 0.7 KB og'ir — va bu 0.7 KB ham marked emas, «Ulashish» tugmasi. marked brauzerga 0 bayt keldi.
Qanday? Markdown'ni HTML'ga aylantirish ishi serverda bajarildi. Brauzerga natija — tayyor HTML — bordi, uni yasagan kod esa serverda qoldi.
Hayotiy o'xshatish: oshxona va zal. Restoranda osh oshxonada pishiriladi — qozon, o'tin, retsept oshxonada qoladi. Mijozga tayyor likopcha keladi. SPA'da esa mijoz stoliga qozon, guruch va retsept kitobi olib kelinadi: "o'zingiz pishiring".
2. Sukut bo'yicha — server
2.1 Siz allaqachon yozgansiz
Yangilik yo'qdek tuyulishi mumkin: Fayl-asosli routing darsidan beri yozgan har sahifangiz (AloqaSahifasi, LoyihaSahifasi) — Server Component. app/ dagi komponent fayl boshida "use client" bo'lmasa — server komponenti. Biz bu nomni ochiq aytmagan edik, xolos.
Server Component qachon ishlaydi?
| Sahifa turi | Server Component qachon chaqiriladi |
|---|---|
Statik (○) |
Build vaqtida, bir marta; keyin tayyor HTML beriladi |
Dinamik (ƒ, ◐ teshigi) |
Har so'rovda, serverda |
Ikkalasida ham brauzerda emas. Statik sahifada komponent hatto foydalanuvchi kelganda ham ishlamaydi — u allaqachon next build paytida ishlab bo'lgan. Bu farqni Rendering strategiyalari darsida o'lchaymiz.
2.2 Ikki xil chiqish
Server Component'ning natijasi brauzerga ikki shaklda boradi:
flowchart LR
S["Server Component<br/>(serverda ishlaydi)"] --> H["HTML<br/>birinchi ochilish"]
S --> R["RSC payload<br/>daraxt matni"]
H --> B["Brauzer: darhol<br/>ko'rinadi"]
R --> B2["React: daraxtni<br/>yig'adi, o'tishlarda"]- HTML — birinchi ochilishda: matn darhol ko'rinadi (Next.js nima darsidagi "bo'sh vitrina" muammosining yechimi).
- RSC payload — React daraxtining maxsus matn formati. React u bilan brauzerdagi daraxtni yig'adi va Navigatsiya darsidagi prefetch so'rovlari aynan shuni oladi.
Ikkalasida ham faqat natija bor, komponentning kodi yo'q. Pastda payload'ni ochib ko'ramiz.
3. async komponent: faylni to'g'ridan o'qish
3.1 Kanondagi blog
Oddiy React'da komponent async bo'la olmaydi — ma'lumot useEffect yoki kutubxona (TanStack Query) orqali keladi. Server Component'da esa async function va await — tabiiy yo'l. Kanondagi post sahifasi:
import { notFound } from "next/navigation";
import { UlashishTugmasi } from "@/komponentlar/UlashishTugmasi";
import { post, postlar } from "@/malumot/blog";
import { oyniYoz } from "@/malumot/sana";
export async function generateStaticParams() {
return (await postlar()).map((p) => ({ slug: p.slug }));
}
export default async function PostSahifasi({
params,
}: PageProps<"/blog/[slug]">) {
const p = await post((await params).slug);
if (!p) notFound();
return (
<main id="asosiy">
<article>
<h1>{p.sarlavha}</h1>
<p className="ikkinchi">
<time dateTime={p.sana}>{oyniYoz(p.sana)}</time> ·{" "}
{p.teglar.join(", ")}
</p>
<div
className="post-matni"
dangerouslySetInnerHTML={{ __html: p.html }}
/>
</article>
<p>
<UlashishTugmasi sarlavha={p.sarlavha} />
</p>
</main>
);
}await post(slug) — komponent ichida to'g'ridan. Yuklanish holati ham, useEffect ham yo'q. generateStaticParams ham async: postlar ro'yxatini fayllardan oladi (generateStaticParams darsida chuqur).
3.2 blog.ts — fayl tizimi komponent yonida
Ma'lumot qatlami — src/malumot/blog.ts. Uning asosiy qismi:
import "server-only";
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
import { marked } from "marked";
import { cacheLife } from "next/cache";
const PAPKA = path.join(process.cwd(), "src", "blog");
export async function post(
slug: string,
): Promise<(Post & { html: string }) | null> {
"use cache";
cacheLife("max");
if (!/^[a-z0-9-]+$/.test(slug)) return null; // yo'l bilan o'ynash (../) yo'q
try {
const { maydonlar, tana } = frontmatter(
await readFile(path.join(PAPKA, `${slug}.md`), "utf8"),
);
return { ...postga(slug, maydonlar), html: await marked.parse(tana) };
} catch {
return null;
}
}Qisqartirilgan: Post turi, frontmatter va postga yordamchilari, postlar() funksiyasi va ikki izoh qatori tushirildi. Kod qatorlari kanondagidek.
| Qator | Nima qiladi |
|---|---|
import "server-only" |
Bu fayl brauzer komponentiga tushsa — build xatosi (Kompozitsiya) |
node:fs/promises |
Node'ning fayl API'si — brauzerda yo'q, serverda bor |
process.cwd() |
Loyiha papkasi; src/blog shu yerdan |
"use cache" + cacheLife("max") |
Natija keshlanadi — pastda sababi |
/^[a-z0-9-]+$/ |
Slug'da ../ bo'lsa — rad: boshqa faylni o'qitib bo'lmaydi |
marked.parse |
Markdown → HTML, serverda |
Slug tekshiruvi — kichik, lekin muhim xavfsizlik qatori. URL'dan kelgan slug to'g'ridan fayl yo'liga tushadi. Kimdir /blog/..%2F..%2F.env so'rasa-chi? Regex faqat kichik harf, raqam va - ga ruxsat beradi — . va / o'tmaydi. Server kodida URL'dagi har qiymat — begona ma'lumot.
frontmatter — post boshidagi --- orasidagi qatorlarni (sarlavha: …, sana: …) o'qiydigan o'z 10 qatorlik funksiya. Kanon gray-matter kutubxonasini olmadi: uning oxirgi relizi 2021-yilda bo'lgan (faol emas).
3.3 Natija oynasida: komponent — oddiy async funksiya
Natija oynasida server yo'q, async komponentni React u yerda chiza olmaydi. Lekin mohiyatini oddiy ts bilan ko'rish mumkin: Server Component — natijasi kutiladigan funksiya. Next.js uni chaqiradi, await qiladi va natijani HTML'ga aylantiradi. Bu yerda JSX o'rniga satr qaytaramiz:
// "fayl tizimi" — xotirada
const FILES: Record<string, string> = {
"salom.md": "# Salom\n\nBirinchi post.",
};
async function readFile(name: string): Promise<string> {
await new Promise((r) => setTimeout(r, 50)); // disk kechikishi
const text = FILES[name];
if (text === undefined) throw new Error(`ENOENT: ${name}`);
return text;
}
// Server Component o'rnida: async, ichida await
async function PostPage({ slug }: { slug: string }) {
if (!/^[a-z0-9-]+$/.test(slug)) return "<h1>Topilmadi</h1>";
const md = await readFile(`${slug}.md`);
const title = md.split("\n")[0].replace("# ", "");
return `<h1>${title}</h1>`;
}
PostPage({ slug: "salom" }).then(console.log);
PostPage({ slug: "../.env" }).then(console.log);Konsolda:
<h1>Topilmadi</h1>
<h1>Salom</h1>Tartibga qarang: ../.env natijasi birinchi chiqdi — u regex'da darhol rad etildi, await gacha yetmadi. salom esa 50 ms "diskni" kutdi. Next.js ham aynan shunday: har komponentni chaqiradi va Promise'lari tugashini kutadi.
Tekshirib ko'ring:
readFilefunksiyasi Server Component'da ishlatilgan. Neganode:fsbrauzerga yuklanib, foydalanuvchi kompyuteridagi fayllarni o'qimaydi?
Javob
Server Component kodi brauzerga umuman bormaydi. readFile serverning (yoki build mashinasining) diskini o'qiydi — src/blog/ dagi postlarni. Brauzer faqat natijani (HTML va payload) oladi. Brauzerning o'zida node:fs yo'q ham.
4. Sirlar serverda qoladi
4.1 Biz tekshirdik
Server Component process.env dagi qiymatni bemalol o'qiydi. U brauzerga chiqmaydimi? Tekshirdik. Tajriba sahifasi:
export default function Sinov5() {
const siri = process.env.SINOV_SIRI ?? "";
return (
<main>
<p>Kalit uzunligi: {siri.length}</p>
</main>
);
}SINOV_SIRI=maxfiy-kalit-777 npx next build, keyin butun .next/ papkasidan shu satrni qidirdik:
static: 0 server: 0
Kalit uzunligi: <!-- -->16<.next/static (brauzerga beriladigan fayllar) — 0 ta fayl, .next/server — ham 0. HTML'da faqat natija: 16. Kalit faqat komponent ishlagan paytda, xotirada bo'ldi. <!-- --> — React'ning matn bo'laklari orasidagi belgisi.
Bitta muhim istisno: NEXT_PUBLIC_ bilan boshlangan o'zgaruvchilar ataylab brauzer to'plamiga yoziladi. Sirni hech qachon shunday nomlamang. Env qoidalari — Env o'zgaruvchilar darsida.
4.2 Sir va dangerouslySetInnerHTML
Post sahifasida dangerouslySetInnerHTML bor — Frontend xavfsizligi React'da darsidan bilasizki, bu xavfli joy. Kanon izohi aniq: matn — o'zimizning fayllarimiz, ishonchli manba. Agar postlar foydalanuvchidan kelsa (izohlar, mehmon postlari), HTML'ni tozalash (sanitizatsiya) shart bo'lardi. Server Component xavfsizlikni o'zi hal qilmaydi — u faqat kod qayerda ishlashini o'zgartiradi.
5. Server Component'ning cheklovlari
5.1 Nima yo'q
Server Component serverda bir marta ishlaydi va natija qaytaradi. Brauzerda u "tirik" emas — demak:
| Mumkin emas | Sababi | O'rniga |
|---|---|---|
useState, useReducer |
Holat brauzerda yashaydi | Client komponent |
useEffect, useLayoutEffect |
Effekt chizilgandan keyin brauzerda ishlaydi | Client komponent |
onClick, onChange |
Hodisa brauzerda | Client komponent |
window, localStorage |
Serverda brauzer API yo'q | Client komponent |
createContext |
Context — client daraxtda | Provider client faylda |
Mumkin: async/await, fayl va baza, sirlar, istalgan og'ir kutubxona, oddiy JSX, client komponentni ichida chizish.
5.2 Biz sinadik: haqiqiy xatolar
Sahifaga oddiy tugma qo'ydik: <button onClick={() => console.log("bosildi")}>. Build:
Error: Event handlers cannot be passed to Client Component props.
{onClick: function onClick, children: ...}
^^^^^^^^^^^^^^^^
If you need interactivity, consider converting part of this to a Client Component.Tarjimasi: "Hodisa ishlovchilarini Client Component prop'lariga uzatib bo'lmaydi. … Interaktivlik kerak bo'lsa, bir qismini Client Component'ga aylantiring". G'alati so'z: "Client Component prop'lari" — biz <button> yozdik-ku? Server uchun <button> ham brauzerda chiziladigan element: u onClick funksiyasini brauzerga yuborishi kerak bo'lardi. Funksiyani esa matnga aylantirib yuborib bo'lmaydi.
useState ning xatosini Loyiha yaratish darsida ko'rgansiz (You're importing a module that depends on useState into a React Server Component module). createContext ham aynan shu xabarni beradi — biz sinadik, faqat so'z boshqa: depends on createContext.
Ikki xato turi bor: hook importi — kompilyatsiya paytida (Turbopack fayl boshidan ko'radi), onClick — prerender paytida (komponent ishga tushganda). Ikkalasining davosi bitta — interaktiv qismni alohida client komponentga ajratish (Client Components darsida).
Tekshirib ko'ring: sahifada joriy yilni ko'rsatish kerak:
{new Date().getFullYear()}. Server Component'da bu mumkinmi?
Javob
Kodning o'zi ishlaydi, lekin statik sahifada yil build vaqtida yoziladi va keyingi build'gacha o'zgarmaydi. Next 16 + Cache Components buni build xatosi bilan ushlaydi (unstable value new Date()) — kanon buni Hydration darsida ko'rsatadi. Tanlov: yilni matnda qoldirish, har so'rovda chizish yoki client'da hisoblash.
6. RSC payload: brauzerga nima boradi?
6.1 Kanon o'lchovi
Kanon /blog/server-components sahifasini ikki usulda so'radi: oddiy (HTML) va RSC: 1 sarlavhasi bilan (Next.js o'tishlarda shunday so'raydi):
HTML 12685 bayt (ichida self.__next_f.push: 8 ta), RSC 200 text/x-component 7528 bayt
marked/micromark nomi payload'da: false; post matni payload'da: true- HTML — 12 685 bayt. Ichida 8 ta
self.__next_f.push(...)skripti: payload HTML'ning ichiga ham joylangan. Brauzer birinchi ochilishda payload uchun alohida so'rov yubormaydi. - RSC javobi — 7 528 bayt, turi
text/x-component. - Payload'da
markedso'zi yo'q, post matni bor. Kutubxona emas, uning natijasi bordi.
6.2 Payload ichida
Xom payload'dan bir nechta qator (to'liq fayl 7 528 bayt):
5:I[35490,["$2"],"NavHavola"]
a:I[61173,["$2","$3"],"OrqagaTugmasi"]
25:I[45089,["$2","$24"],"UlashishTugmasi"]
11:["$","main",null,{"id":"asosiy","children":[["$","article",null,{"children":[["$","h1",null,{"children":"Server Components — JavaScript'siz React"}],…
…["$","$L25",null,{"sarlavha":"Server Components — JavaScript'siz React"}]Oxirgi ikki qator qisqartirilgan (…). O'qishni o'rganamiz:
| Bo'lak | Ma'nosi |
|---|---|
5: |
Qator raqami (16 lik sanoqda) — boshqa qatorlar unga $5 deb murojaat qiladi |
I[35490,[…],"NavHavola"] |
Client komponentga havola: modul raqami, JS fayllari, eksport nomi |
["$","h1",null,{"children":…}] |
Oddiy element: tur, kalit, props — tayyor matni bilan |
["$","$L25",null,{"sarlavha":…}] |
25-qatordagi client komponent (UlashishTugmasi) va unga props |
Server komponentlar (PostSahifasi, LoyihaKartasi) payload'da umuman ko'rinmaydi — faqat ular qaytargan elementlar (main, article, h1). Client komponentlar esa nom bilan, "bu yerga shu faylni yuklab, shu props bilan chiz" degan havola bo'lib turadi. Brauzer faqat shu havolalarning JS'ini yuklaydi.
$L — "lazy" (kechiktirilgan) havola: komponent kodi alohida faylda, kerak bo'lganda yuklanadi.
Bu format — React'ning ichki tafsiloti, uni qo'lda yozmaysiz. Lekin uni o'qiy olish foydali: DevTools'ning Network panelida ?_rsc= so'rovlarini ochsangiz, "nega bu sahifa og'ir?" degan savolga javob ko'rinadi.
6.3 Raqamlar: brauzer JS
| Sahifa | JS (gzip) | Farqi |
|---|---|---|
| Boshqa sahifalar | 137.9 KB | — |
/blog/server-components |
138.6 KB | +0.7 KB (UlashishTugmasi) |
marked client komponentda (biz sinadik) |
alohida bo'lak 13.3 KB | butun kutubxona |
Kutubxona tanlovi ham o'lchandi: marked 18.1.1 — 540 KB, 1 paket; @next/mdx + loader — 9.8 MB, 104 paket. Ikkalasi ham faqat serverda ishlaydi, brauzerga farqi yo'q — lekin node_modules va build tezligiga bor. Postlarda React komponenti kerak emas edi, shuning uchun kanon marked ni tanladi.
7. Biz topdik: fayl o'qish ham "keshlanmagan ma'lumot"
blog.ts dagi "use cache" qatori nega kerak? Kanon avval usiz build qildi:
Error: Route "/blog": Next.js encountered uncached or runtime data during prerendering.
`fetch(...)`, `cookies()`, `headers()`, `params`, `searchParams`, or `connection()` accessed outside of `<Suspense>` prevents the route from being prerendered, blocking the page load and leading to a slower user experience.
Ways to fix this:
- [stream] Provide a placeholder with `<Suspense fallback={...}>` around the data access
- [cache] For uncached data (`fetch`, database calls): cache the access with `"use cache"` (does not apply to `connection()`)
- [block] Set `export const instant = false` to allow a blocking routeCache Components rejimida Next.js har asinxron ma'lumot manbasini — fetch, baza, hatto disk — "o'zgarishi mumkin" deb hisoblaydi. U sahifani oldindan chizishdan oldin aniq bilishni xohlaydi: bu ma'lumot keshlansinmi ([cache]), har so'rovda kutilsinmi ([stream]) yoki sahifa umuman bloklovchi bo'lsinmi ([block]).
Postlar faqat deploy bilan o'zgaradi — kanon [cache] ni tanladi: "use cache" + cacheLife("max"). Build jadvalida yangi ustunlar paydo bo'ldi:
Route (app) Revalidate Expire
├ ○ /blog 30d 1y
│ ├ ○ /blog/server-components 30d 1y30d / 1y — max profilining muddatlari. "use cache" ning o'zi — Cache Components I darsida. Bugun bilish yetarli: Server Component'da ma'lumot olish — oson, lekin Next 16 "qachon yangilanadi?" degan savolni javobsiz qoldirmaydi.
8. Portfolio qadami: blog (1-qism)
Bu dars, Client Components va Kompozitsiya darslari portfolio ning holat 11 ini quradi. Commit hali qilmang — u uchinchi darsda.
| Fayl | Nima |
|---|---|
src/blog/*.md |
3 post: frontmatter (sarlavha, sana, tavsif, teglar) + Markdown |
src/malumot/blog.ts |
server-only, fs, o'z frontmatter tahlili, marked, "use cache" |
app/(sayt)/blog/page.tsx |
Ro'yxat — async Server Component |
app/(sayt)/blog/[slug]/page.tsx |
Post — generateStaticParams, notFound() |
komponentlar/SaytSarlavhasi.tsx |
Menyuga «Blog» |
npm i --save-exact marked@18.1.1 server-only@0.0.1Build jadvalida ○ /blog va uchta ○ /blog/<slug> (30d/1y), ◐ /blog/[slug] — noma'lum slug uchun qobiq. Postlaringiz — o'zingizniki: o'rgangan narsangiz haqida uchta qisqa yozuv.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Event handlers cannot be passed to Client Component props |
Server Component'da onClick |
Tugmani client komponentga ajrating |
… depends on useState / createContext into a React Server Component |
Hook yoki Context serverda | "use client" li alohida fayl |
uncached or runtime data during prerendering |
Fayl/fetch keshsiz, Suspense'siz | "use cache" yoki <Suspense> |
ReferenceError: window is not defined |
Server kodida brauzer API | Brauzer API — client komponentda, effekt ichida |
| Sir brauzerda ko'rindi | NEXT_PUBLIC_ prefiksi |
Sirni prefikssiz nomlang |
/blog/..%2F.env fayl o'qidi |
Slug tekshirilmagan | Qat'iy regex (^[a-z0-9-]+$) |
10. Mashqlar
Mashqlar kurs/mashqlar/23/09-server-components/ da.
1-mashq (oson): server yoki client?
- Postni Markdown'dan HTML'ga aylantirish —
- «Nusxalash» tugmasi (
navigator.clipboard) — process.env.GITHUB_TOKENbilan API so'rovi —- Hisoblagich (
useState) — - Loyihalar ro'yxatini massivdan chizish —
Yechim
- server — kutubxona brauzerga bormaydi, natija HTML bo'lib keladi.
- client — brauzer API va bosish hodisasi.
- server — token faqat serverda xavfsiz.
- client — holat brauzerda yashaydi.
- server — interaktivlik yo'q; sukut bo'yicha server, JS 0.
2-mashq (o'rta): frontmatter tahlili
Kanondagidek o'z frontmatter(text) funksiyangizni yozing: --- orasidagi kalit: qiymat qatorlarini obyektga yig'ing, qolgan matnni body deb qaytaring. Frontmatter bo'lmasa — { fields: {}, body: text }. Qiymat ichida : bo'lishi mumkin (tavsif: Next.js: birinchi qadam) — faqat birinchi : bo'yicha bo'ling. Ishora: indexOf va slice.
Yechim
function frontmatter(text) {
const m = /^---\n([\s\S]*?)\n---\n/.exec(text);
if (!m) return { fields: {}, body: text };
const fields = {};
for (const line of m[1].split("\n")) {
const i = line.indexOf(":");
fields[line.slice(0, i).trim()] = line.slice(i + 1).trim();
}
return { fields, body: text.slice(m[0].length) };
}
const post = `---
sarlavha: Salom
tavsif: Next.js: birinchi qadam
---
# Salom`;
const { fields, body } = frontmatter(post);
console.log(fields.sarlavha);
console.log(fields.tavsif);
console.log(body);
console.log(frontmatter("# Frontmattersiz").fields);Konsolda:
Salom
Next.js: birinchi qadam
# Salom
{}indexOf(":") birinchi ikki nuqtani topadi — tavsif qiymatidagi Next.js: buzilmadi. Kanon versiyasi \r?\n ham qabul qiladi: Windows'da saqlangan fayllarda qator oxiri \r\n bo'ladi.
3-mashq (qiyin): payload modelini yasang
RSC payload'ning soddalashtirilgan modeli: toPayload(node) — daraxtni oladi va qatorlar massivini qaytaradi. Tugun — satr (matn) yoki { type, props, children }. type — satr ("h1") bo'lsa — oddiy element; { client: "Nom" } bo'lsa — client komponent: unga alohida qator N:I["Nom"] yoziladi va o'rnida "$LN" turadi. Funksiya bo'lsa (type: function) — bu Server Component: uni chaqirib, natijasini yozing (payload'da server komponent ko'rinmaydi). Oxirgi qator — 0: + daraxt JSON'i.
Yechim
function toPayload(root) {
const rows = [];
let next = 1;
function walk(node) {
if (typeof node === "string") return node;
if (typeof node.type === "function") {
return walk(node.type(node.props)); // server: chaqiramiz
}
const children = (node.children ?? []).map(walk);
if (typeof node.type === "object") {
const id = next++;
rows.push(`${id}:I["${node.type.client}"]`);
return ["$", `$L${id}`, node.props ?? {}, children];
}
return ["$", node.type, node.props ?? {}, children];
}
const tree = walk(root);
rows.push(`0:${JSON.stringify(tree)}`);
return rows;
}
const Share = { client: "UlashishTugmasi" };
function PostPage(props) {
return {
type: "main",
children: [
{ type: "h1", children: [props.title] },
{ type: Share, props: { sarlavha: props.title } },
],
};
}
const rows = toPayload({ type: PostPage, props: { title: "Salom" } });
for (const row of rows) console.log(row);Konsolda:
1:I["UlashishTugmasi"]
0:["$","main",{},[["$","h1",{},["Salom"]],["$","$L1",{"sarlavha":"Salom"},[]]]]PostPage payload'da yo'q — u chaqirildi va o'rnida natijasi (main) qoldi. UlashishTugmasi esa havola ($L1) va props bo'lib qoldi. Haqiqiy format murakkabroq (modul raqamlari, JS fayllar ro'yxati, kalitlar), lekin g'oyasi shu: server komponentlar "eriydi", client komponentlar "manzil" bo'lib qoladi.
4-mashq: Portfolio qadami
O'z portfolio ingizga blog qo'shing: src/blog/ da 3 ta post, src/malumot/blog.ts (server-only, fs, marked, slug tekshiruvi, "use cache"), ro'yxat va post sahifalari, menyuda «Blog». Tekshiring: npm run build jadvalida /blog 30d/1y bilan; next start da DevTools Network panelida JS fayllar orasida marked kodi yo'qligini toping (Search: marked).
Yechim
Namuna — «Portfolio qadami» bo'limidagi jadval va kanon blog.ts. Build uncached or runtime data bilan to'xtasa — "use cache" qatori funksiyaning birinchi qatori bo'lishi kerak (direktiva, "use client" kabi). Network'da marked topilsa — uni qaysidir client komponent import qilgan (Client Components darsida sababini ko'ramiz). Commit hali qilmang.
11. Real ishda
- Do'kon va kontent saytlari — mahsulot sahifasi Server Component: bazadan narx va tavsif, brauzerga faqat «Savatga» tugmasining JS'i. Katta katalogda JS farqi o'nlab kilobayt.
- Sirlar — to'lov tizimi kaliti, baza paroli, AI API tokeni — hammasi Server Component va server kodida; brauzerga hech qachon. Intervyuda "API kalitini frontend'da qanday yashirasiz?" savolining zamonaviy javobi shu.
- RSC payload'ni o'qish — sahifa nega og'irligini topishda: payload'da kutilmagan katta ma'lumot (butun mahsulotlar ro'yxati) client komponentga prop bo'lib ketayotgan bo'lishi mumkin.
- React Server Components — faqat Next.js'niki emas: React 19 ning qismi, boshqa freymvorklar ham qo'llab-quvvatlashni boshlagan.
Xulosa
app/dagi komponent sukut bo'yicha Server Component: build vaqtida yoki serverda ishlaydi, brauzerga kodi bormaydi.asyncbo'la oladi: fayl, baza, API va sirlar to'g'ridan —markedbrauzerga 0 bayt, sir HTML'da faqat natija.useState,useEffect,onClick,window,createContext— yo'q; xatolari aniq, davosi — client komponent.- Brauzerga HTML va RSC payload boradi: server komponentlar eriydi, client komponentlar
I[…]havola bo'lib qoladi. - Cache Components'da fayl o'qish ham "keshlanmagan ma'lumot" —
"use cache"yoki<Suspense>tanlanadi.
Keyingi dars: Client Components va "use client" chegarasi — «Ulashish» tugmasi qanday qilib brauzerda jonlanadi va "use client" qatori nimani "yuqtiradi".
Manbalar
- Next.js hujjati — Server and Client Components: https://nextjs.org/docs/app/getting-started/server-and-client-components (next 16.4.0 paketi ichidagi hujjat, 2026-10-21 da o'qildi)
- React hujjati — Server Components: https://react.dev/reference/rsc/server-components
- Kanon o'lchovlari:
portfoliov2, holat 11 (RSC payload, JS hajmi, kutubxona tanlovi, fs keshsiz build xatosi).onClick,createContext, sir vamarkedclient'da tajribalari — biz ishga tushirdik (next 16.4.0,next build).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!