Mundarija (36)
- Bu darsda
- 1. Nega bu kerak?
- 2. Reja: komponentlar va state
- 2.1 Talablar
- 2.2 State — minimal
- 2.3 Komponentlar daraxti
- 3. Model: sof funksiyalar
- 4. Komponentlar
- 5. App — hammasi birga
- 5.1 Boshqa ssenariylar
- 6. Vazifalar qadami: butun UI React'da
- 6.1 Nima qoldi
- 6.2 TemaTanlov.tsx
- 6.3 App va index.html
- 6.4 Tekshiruv va commit
- 7. Vanilla va React: o'lchangan taqqoslash
- 7.1 Hajm
- 7.2 Kod
- 7.3 Tezlik: 1 000 vazifa
- 7.4 Lighthouse
- 7.5 Xulosa: nima berdi, nima oldi
- 8. Vercel'ga deploy
- 8.1 Vercel nima
- 8.2 vercel.json — kanondan
- 8.3 Qadamlar (Vercel hujjatidan)
- 8.4 Deploy'dan keyin tekshiring
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): Raqamlarni o'qing
- 2-mashq (o'rta): "Bajarilganlarni tozalash"
- 3-mashq (qiyin): Muhim vazifalar
- 4-mashq: Vazifalar qadami
- 5-mashq: Vercel'ga deploy (o'z hisobingiz bilan)
- 11. Real ishda
- Xulosa
- Manbalar
Amaliy: birinchi to'liq React mini-ilova — vazifalar ro'yxati va Vercel
Qisqacha: Bu darsda 17-qismning birinchi yarmi bitta ilovada birlashadi. Avval kichik vazifalar ro'yxatini noldan yozamiz: sof funksiyalar (o'zgarmas yangilash), state tuzilmasi (vazifalar, filtr, tahrirdagi id), uncontrolled +
FormDataforma, controlled tahrir,keyva hosilaviy hisob. Keyin kursdagivazifalarilovasining butun interfeysi React'ga o'tadi va uni vanilla versiya bilan o'lchab solishtiramiz. Kod 14.3 KB dan 83.1 KB ga (gzip) o'sdi, lekin 1 000 vazifada bitta katakni belgilash 47–77 ms dan 19–20 ms ga tushdi. Oxirida ilovani o'z hisobingiz bilan Vercel'ga joylaysiz —vercel.jsontayyor.
Bu darsda
- To'liq CRUD ilova yozasiz: qo'shish, belgilash, tahrirlash, o'chirish, filtr, hisob.
- Har qarorni oldingi darslar bilan bog'laysiz: qayerda state, qayerda props, qaysi forma controlled.
vazifalarda oxirgi vanilla bo'laklarni (tema tanlovi, izoh oroli) React'ga ko'chirasiz.- Vanilla va React'ni haqiqiy raqamlar bilan taqqoslaysiz: hajm, kod, tezlik, Lighthouse.
vercel.jsonni o'qiysiz va ilovani Vercel'ga deploy qilasiz.
Oldin bilishingiz kerak: Kichik React loyihani tashkil qilish, Formalar I, Formalar II, State'ning saqlanishi, State strukturasi.
1. Nega bu kerak?
23 dars davomida React'ni bo'lakma-bo'lak o'rgandik: JSX, komponentlar, props, ro'yxat va key, hodisalar, useState, state tuzilmasi, formalar. Lekin haqiqiy ishda bular alohida kelmaydi: bitta ekranda forma, ro'yxat, filtr va hisob bir-biriga bog'langan. Qaysi state qayerda turadi, kim kimga nima beradi — bu savollarga faqat butun ilova yozganda duch kelasiz.
Shuning uchun bu dars ikki qismdan iborat. Birinchisida kichik ilovani noldan yozamiz — hamma qadam ko'z oldingizda. Ikkinchisida kursdagi vazifalar ning React'ga ko'chishini yakunlaymiz. Ilova 9-qismdan beri vanilla edi va 17/#02 dan boshlab asta-sekin React'ga o'tdi (Birinchi React loyiha darsidagi orol). Oxirida esa savolga halol javob beramiz: React bizga nima berdi va nima oldi?
Hayotiy o'xshatish: oshpazlik kursida har darsda bitta usul — to'g'rash, qovurish, dimlash. Bugun — birinchi to'liq osh, hamma usul bitta qozonda.
2. Reja: komponentlar va state
2.1 Talablar
«Bahor» oshxonasining ichki vazifalar ro'yxati:
- yangi vazifa qo'shish (bo'sh yoki takror matn — xato xabari);
- bajarildi deb belgilash;
- matnni tahrirlash (Enter — saqlash, Esc — bekor);
- o'chirish;
- filtr: hammasi / faol / bajarilgan;
- pastda: "N ta qoldi, M ta bajarildi".
2.2 State — minimal
State strukturasi darsidagi savol: nima saqlanadi, nima hisoblanadi?
| Ma'lumot | State yoki hosilaviy qiymat | Nega |
|---|---|---|
| vazifalar ro'yxati | state | foydalanuvchi o'zgartiradi |
| tanlangan filtr | state | foydalanuvchi tanlaydi |
| qaysi vazifa tahrirda | state — id | obyekt emas, id (takrorlamang) |
| ko'rinadigan vazifalar | hosilaviy qiymat | tasks + filter dan |
| "N ta qoldi" | hosilaviy qiymat | tasks dan |
| tahrirdagi matn | EditRow ning state'i |
faqat u ishlatadi (colocation) |
| forma xatosi | NewTaskForm ning state'i |
faqat u ko'rsatadi |
Uchta state App da, ikkitasi — o'z komponentida. Hammasi bitta joyda emas: State'ni ko'tarish darsidagi "keragidan baland emas" qoidasi.
2.3 Komponentlar daraxti
flowchart TD
A["App: tasks, filter, editId"] --> F["NewTaskForm: error"]
A --> B["FilterBar"]
A --> L["ul — shown.map"]
L --> V["ViewRow"]
L --> E["EditRow: text"]
A --> S["Stats"]Ma'lumot pastga (props), hodisalar yuqoriga (onAdd, onChange, onToggle, onSave). ViewRow va EditRow — bir o'rinda ikki tur: tahrir boshlanganda EditRow yangidan yaratiladi va vazifaning joriy matnini oladi (State'ning saqlanishi).
3. Model: sof funksiyalar
Avval React'siz qism — ma'lumot va uni o'zgartiradigan funksiyalar. Ular immutable yangilash qoidasiga bo'ysunadi: hech biri massivni o'zgartirmaydi, hammasi yangisini qaytaradi.
// model — ma'lumot va sof funksiyalar (React'siz)
export type Task = { id: number; text: string; done: boolean };
export type Filter = "hammasi" | "faol" | "bajarilgan";
type Tasks = readonly Task[]; // faqat o'qish uchun massiv
export const FILTERS: readonly Filter[] = [
"hammasi",
"faol",
"bajarilgan",
];
function nextId(tasks: Tasks): number {
return Math.max(0, ...tasks.map((t) => t.id)) + 1;
}
export function addTask(tasks: Tasks, text: string): Task[] {
return [...tasks, { id: nextId(tasks), text, done: false }];
}
export function toggleTask(tasks: Tasks, id: number): Task[] {
return tasks.map((t) =>
t.id === id ? { ...t, done: !t.done } : t,
);
}
export function renameTask(
tasks: Tasks,
id: number,
text: string,
): Task[] {
return tasks.map((t) => (t.id === id ? { ...t, text } : t));
}
export function removeTask(tasks: Tasks, id: number): Task[] {
return tasks.filter((t) => t.id !== id);
}
export function visibleTasks(tasks: Tasks, filter: Filter): Tasks {
if (filter === "faol") return tasks.filter((t) => !t.done);
if (filter === "bajarilgan") return tasks.filter((t) => t.done);
return tasks;
}
export function validate(
tasks: Tasks,
text: string,
exceptId: number | null = null,
): string | null {
if (text === "") return "Vazifa matnini yozing";
if (text.length > 100) return "100 belgidan oshmasin";
const lower = text.toLowerCase();
const same = tasks.some(
(t) => t.id !== exceptId && t.text.toLowerCase() === lower,
);
return same ? "Bunday vazifa bor" : null;
}
const start: Task[] = [{ id: 1, text: "Non olish", done: false }];
const a = addTask(start, "Sut olish");
const b = toggleTask(a, 1);
console.log(b.map((t) => `${t.id}:${t.text}:${t.done}`).join(" | "));
console.log(start[0].done, visibleTasks(b, "faol").length);
console.log(validate(b, "non olish"), validate(b, "Choy"));Konsolda:
1:Non olish:true | 2:Sut olish:false
false 1
Bunday vazifa bor nullOxirgi uch qator — kichik sinov. Birinchi vazifaning asl nusxasi (start[0].done) hali false: toggleTask asl massivga tegmadi. Tekshiruv funksiyasi takror matnni katta-kichik harfga qaramay topdi ("non olish"). Uchinchi parametr exceptId — tahrirda vazifaning o'zi bilan solishtirmaslik uchun: "Non olish" ni "Non olish" ga saqlash — takror emas.
Tasks = readonly Task[] — TypeScript da "bu massivni o'zgartirma": tasks.push(…) yozsangiz, tsc to'xtatadi. nextId — eng katta id + 1. Bu oddiy yo'l, lekin eng kattasi o'chirilsa, uning id'si qayta berilishi mumkin. vazifalar da ham shunday, va bu TEXNIK-QARZ ga yozilgan (server id'ni o'zi beradi).
Maslahat: Sof funksiyalarni React'dan ajratib yozish — Kichik React loyihani tashkil qilish dagi
lib/g'oyasi. Haqiqiy loyihada bu qismsrc/lib/tasks.tsbo'lardi vanode --testbilan sinalardi.
4. Komponentlar
Endi React qismi. Quyidagi blok oldingisining davomi — model funksiyalari shu yerda ishlatiladi.
import { useState } from "react";
import type { FormEvent, KeyboardEvent } from "react";
type NewTaskFormProps = { onAdd: (text: string) => string | null };
// Uncontrolled + FormData: qiymat faqat yuborilganda kerak
function NewTaskForm({ onAdd }: NewTaskFormProps) {
const [error, setError] = useState<string | null>(null);
function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
const form = e.currentTarget;
const text = String(new FormData(form).get("text") ?? "").trim();
const problem = onAdd(text);
setError(problem);
if (problem === null) form.reset(); // defaultValue yo'q
}
return (
<form onSubmit={handleSubmit} noValidate>
<label>
Vazifa <input name="text" aria-invalid={error !== null} />
</label>{" "}
<button>Qo'sh</button>
<p role="status">{error}</p>
</form>
);
}
type FilterBarProps = {
filter: Filter;
onChange: (f: Filter) => void;
};
const FILTER_NAMES: Record<Filter, string> = {
hammasi: "Hammasi",
faol: "Faol",
bajarilgan: "Bajarilgan",
};
// Controlled: tanlov otada, tugmalar faqat xabar beradi
function FilterBar({ filter, onChange }: FilterBarProps) {
return (
<p role="group" aria-label="Filtr">
{FILTERS.map((f) => (
<button
key={f}
aria-pressed={f === filter}
onClick={() => onChange(f)}
>
{FILTER_NAMES[f]}
</button>
))}
</p>
);
}
type EditRowProps = {
task: Task;
onSave: (text: string) => void;
onCancel: () => void;
};
// Controlled tahrir: matn state'da, boshlang'ichi — vazifa matni
function EditRow({ task, onSave, onCancel }: EditRowProps) {
const [text, setText] = useState(task.text);
function handleKey(e: KeyboardEvent<HTMLInputElement>) {
if (e.key === "Enter") onSave(text);
if (e.key === "Escape") onCancel();
}
return (
<li>
<input
aria-label="Matn"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={handleKey}
/>{" "}
<button onClick={() => onSave(text)}>Saqla</button>
<button onClick={onCancel}>Bekor</button>
</li>
);
}
type ViewRowProps = {
task: Task;
onToggle: () => void;
onEdit: () => void;
onRemove: () => void;
};
function ViewRow({ task, onToggle, onEdit, onRemove }: ViewRowProps) {
return (
<li>
<label>
<input
type="checkbox"
checked={task.done}
onChange={onToggle}
/>
{task.done ? <s>{task.text}</s> : task.text}
</label>{" "}
<button onClick={onEdit} aria-label={`Tahrir ${task.text}`}>
✎
</button>
<button onClick={onRemove} aria-label={`O'chir ${task.text}`}>
✕
</button>
</li>
);
}
function Stats({ tasks }: { tasks: Tasks }) {
const done = tasks.filter((t) => t.done).length; // hosilaviy
return (
<p aria-live="polite">
{tasks.length - done} ta qoldi, {done} ta bajarildi
</p>
);
}Har komponentda bitta darsning izi:
| Komponent | Nima ishlatadi | Dars |
|---|---|---|
NewTaskForm |
uncontrolled, FormData, noValidate, reset() |
Formalar II |
FilterBar |
controlled komponent, aria-pressed, Record |
State'ni ko'tarish |
EditRow |
controlled input, useState(task.text), Enter/Esc |
Formalar I |
ViewRow |
controlled checkbox, aria-label, callback props |
Hodisalar |
Stats |
hosilaviy qiymat | State strukturasi |
form.reset() bu yerda xavfsiz: maydonda defaultValue yo'q, demak "boshlang'ich qiymat" — bo'sh satr. Formalar II dagi tuzoq faqat defaultValue bo'lganda. Belgi tugmalari (, ) faqat ko'rinish uchun. Malikaning ekran o'quvchisi aria-label ni o'qiydi: "Tahrir Non olish". Har qatorda tugma nomi boshqa — "qaysi vazifaning tugmasi?" degan savol yo'q.
5. App — hammasi birga
const START: Task[] = [
{ id: 1, text: "Non olish", done: false },
{ id: 2, text: "Onamga qo'ng'iroq", done: false },
];
export default function App() {
const [tasks, setTasks] = useState<Task[]>(START);
const [filter, setFilter] = useState<Filter>("hammasi");
const [editId, setEditId] = useState<number | null>(null);
const shown = visibleTasks(tasks, filter); // hosilaviy
function handleAdd(text: string): string | null {
const problem = validate(tasks, text);
if (problem === null) setTasks(addTask(tasks, text));
return problem;
}
function handleSave(id: number, text: string) {
const clean = text.trim();
if (validate(tasks, clean, id) !== null) return;
setTasks(renameTask(tasks, id, clean));
setEditId(null);
}
return (
<main>
<h1>Vazifalar</h1>
<NewTaskForm onAdd={handleAdd} />
<FilterBar filter={filter} onChange={setFilter} />
<ul>
{shown.map((t) =>
t.id === editId ? (
<EditRow
key={t.id}
task={t}
onSave={(text) => handleSave(t.id, text)}
onCancel={() => setEditId(null)}
/>
) : (
<ViewRow
key={t.id}
task={t}
onToggle={() => setTasks(toggleTask(tasks, t.id))}
onEdit={() => setEditId(t.id)}
onRemove={() => setTasks(removeTask(tasks, t.id))}
/>
),
)}
</ul>
{shown.length === 0 && <p>Bu yerda vazifa yo'q.</p>}
<Stats tasks={tasks} />
</main>
);
}Ekranda:
Vazifalar
Vazifa Qo'sh
HammasiFaolBajarilgan
Non olish ✎✕
Onamga qo'ng'iroq ✎✕
Sut olish ✎✕
2 ta qoldi, 1 ta bajarildi«Ishga tushir» ni bosib, ilovani o'zingiz sinab ko'ring. Pastdagi natija — "Sut olish" qo'shilib, "Non olish" belgilangandan keyingi holat (ekran natijasida <s> ichidagi matn oddiy matn bo'lib ko'rinadi). Qadamma-qadam:
| Qadam | tasks |
filter, editId |
Ekranda |
|---|---|---|---|
| boshida | 2 ta, hammasi faol | hammasi, null |
2 qator, "2 ta qoldi, 0 ta bajarildi" |
| "Sut olish" qo'shildi | 3 ta | hammasi, null |
3 qator, "3 ta qoldi" |
| "Non olish" belgilandi | 1-si done: true |
hammasi, null |
"2 ta qoldi, 1 ta bajarildi" |
5.1 Boshqa ssenariylar
Xuddi shu ilova, boshqa harakatlar bilan. Tahrir: "Non olish" → "Lavash".
// (yuqoridagi ilova — boshqa ssenariy)Ekranda:
Vazifalar
Vazifa Qo'sh
HammasiFaolBajarilgan
Lavash ✎✕
Onamga qo'ng'iroq ✎✕
2 ta qoldi, 0 ta bajarildiTakror vazifa va filtr:
// (yuqoridagi ilova — boshqa ssenariy)Ekranda:
Vazifalar
Vazifa Qo'sh
Bunday vazifa bor
HammasiFaolBajarilgan
Non olish ✎✕
Onamga qo'ng'iroq ✎✕
2 ta qoldi, 0 ta bajarildiXato xabari chiqdi va vazifa qo'shilmadi. NewTaskForm reset() qilmadi — "non olish" matni maydonda qoldi, tuzatish mumkin.
Tekshirib ko'ring: "Faol" filtrida "Non olish" ni belgilasak, u ro'yxatdan yo'qoladi. Agar o'sha paytda boshqa vazifa tahrirda bo'lsa, uning yozilayotgan matni saqlanadimi?
Javob
Ha. EditRow ning key i — vazifa id'si, u o'zgarmadi. Boshqa qator ro'yxatdan chiqqani uchun tahrirdagi qator o'rni siljiydi, lekin React uni key bo'yicha taniydi va state'ini (text) saqlaydi. key={index} bo'lganda esa matn yo'qolardi (State'ning saqlanishi, 3-mashq).
6. Vazifalar qadami: butun UI React'da
6.1 Nima qoldi
Kichik React loyihani tashkil qilish darsidan keyingi holatda vazifalar ning deyarli hamma qismi React'da. index.html da faqat ikki bo'lak qolgan:
- tema tanlovi (
<fieldset id="tema-tanlov" hidden>— tizim/yorug'/qorong'i radio tugmalari), uni hali vanillatemaniUla()boshqaradi; - izoh oroli (
<div id="izoh">) — Birinchi React loyiha darsidagi birinchi komponent, alohidacreateRootbilanmain.tsxdan.
Yangi branch: feat/react-ui.
6.2 TemaTanlov.tsx
Kanondan (kod aynan, izoh qatorlari telefon uchun qayta bo'lingan):
// TemaTanlov.tsx — tizim / yorug' / qorong'i (13/#05). 17/#24 da
// React'ga ko'chdi: qaysi tugma belgilangani — state
// (controlled radio)
import { Fragment, useState } from "react";
import type { ChangeEvent } from "react";
import {
TEMALAR,
saqlanganTanlov,
tanlovniOl,
tanlovniSaqla,
} from "../tema-tanlash.ts";
import type { Tema } from "../tema-tanlash.ts";
const NOMLAR: Readonly<Record<Tema, string>> = Object.freeze({
tizim: "Tizim",
yorug: "Yorug'",
qorongi: "Qorong'i",
});
export function TemaTanlov() {
// Funksiya — boshlang'ich qiymatni faqat BIRINCHI renderda
// hisoblaydi (localStorage har renderda o'qilmaydi)
const [tanlov, setTanlov] = useState(saqlanganTanlov);
function tanlandi(event: ChangeEvent<HTMLInputElement>) {
const yangi = tanlovniOl(event.target.value);
setTanlov(yangi);
tanlovniSaqla(yangi); // xotira, <html data-tema>, panel rangi
}
// v6 da fieldset index.html da hidden edi va JS uni ochardi
// (JS ishlamasa — tugmalar ko'rinmasin). Endi uni React chizadi:
// JS yo'q bo'lsa, umuman yo'q
return (
<fieldset id="tema-tanlov">
<legend>Tema</legend>
{TEMALAR.map((tema) => (
<Fragment key={tema}>
<input
type="radio"
name="tema"
id={`tema-${tema}`}
value={tema}
checked={tanlov === tema}
onChange={tanlandi}
/>
<label htmlFor={`tema-${tema}`}>{NOMLAR[tema]}</label>
</Fragment>
))}
</fieldset>
);
}Bu yerda yangi narsa bitta — useState(saqlanganTanlov). Qavslarsiz! saqlanganTanlov() chaqirilmagan, funksiyaning o'zi berilgan. React uni faqat birinchi renderda chaqiradi va natijasini boshlang'ich qiymat qiladi — bu lazy initialization ("dangasa boshlash"). useState(saqlanganTanlov()) yozilsa ham ishlaydi, lekin localStorage har renderda o'qiladi va natijasi tashlab yuboriladi. Qolgani tanish: controlled radio (Formalar I), <Fragment key> (JSX II) — har tema uchun input va label juftligi.
tema-tanlash.ts dagi temaniUla() ikkiga bo'lindi: radio tugmalarga tegadigan qismi — endi komponentda. <html data-tema> va telefon panelining rangi (theme-color) esa temaniQolla() da qoldi — bu sahifa sozlamasi, React daraxtidan tashqarida. asosiy.ts ishga tushganda:
// Panel rangi (theme-color) — saqlangan tanlov bo'yicha; <html
// data-tema> ni tema.js allaqachon qo'ygan
temaniQolla(saqlanganTanlov());
tizimTemasiniKuzat();
render();6.3 App va index.html
App.tsx ga ikki qator:
<Import onImport={amallar.onImport} />
<OchirishDialog matn={holat.ochirilayotgan} />
+ <TemaTanlov />
+ <Izoh />
</>index.html dagi <main> endi shunday:
<main>
<h1>Vazifalar</h1>
- <!-- React ildizi (17/#16): aloqa, forma, qidiruv, filtr, ro'yxat,
- hisob, e'lon, eksport/import (#19), dialog — src/App.tsx -->
+ <!-- React ildizi: butun ilova — src/App.tsx (17/#16, #24) -->
<div id="ilova"></div>
- <fieldset id="tema-tanlov" hidden>
…
- <!-- React oroli (17/#02): ichini src/main.tsx chizadi -->
- <div id="izoh"></div>
</main>(… — radio tugmalarining 7 qatori.) Yana ikki o'zgarish bor. <head> dagi main.tsx skripti olib tashlandi va fayl o'chirildi — izoh endi alohida orol emas, App ichida. Sahifa tavsifi (<meta name="description">) ham yangilandi: "React'da yozilgan vazifalar ro'yxati." main.tsx keyinroq butun ilovaning yagona kirish nuqtasi bo'lib qaytadi, state App ga ko'chganda (useEffect III).
<body> ichida endi 7 qator HTML. Sahifadagi har element — sarlavhadan tashqari — React chizadi.
6.4 Tekshiruv va commit
npm run build && npm test && npm run lint && npm run tipKanon natijalari: node:test — 149/149, Vitest — 22/22, brauzer tekshiruvi — 159/159 (tema tanlovi, oflayn rejim, CSP va boshqalar o'zgarishsiz). Diff: 7 fayl, +101 −72.
feat: to'liq React UI — tema tanlovi va izoh App ichidaBody: "TemaTanlov (controlled radio, useState lazy init); index.html <main> — <h1> + <div id="ilova">. main.tsx oroli olib tashlandi. Vanilla DOM kodi qolmadi." PR'ni birlashtiring.
7. Vanilla va React: o'lchangan taqqoslash
Endi bir xil ilovaning ikki versiyasi bor: v6 (16-qism, vanilla + TypeScript) va hozirgi holat (React). Ikkalasi bir xil 159 brauzer tekshiruvidan o'tadi — xulqi bir xil. Farqni o'lchadik (Chrome 154 headless, Intel i5-12500H noutbuk).
7.1 Hajm
Brauzer yuklaydigan JS va CSS (build natijasi, bayt):
| v6 (vanilla) | React (shu holat) | |
|---|---|---|
| xom | 38 066 | 261 396 |
| gzip -9 | 14 279 | 83 107 |
| brotli | 13 033 | 72 012 |
Taxminan 6–7 barobar. Qayerdan? npm run analiz (Vite V: bundle tahlili) javob beradi — minifikatsiyadan oldingi hajm ulushi:
| Modul | Ulush |
|---|---|
react-dom-client.production.js |
83.8% (517 KiB) |
react.production.js |
2.5% |
web-vitals |
1.8% |
scheduler |
1.4% |
bizning asosiy.ts |
1.3% |
O'sishning deyarli hammasi — react-dom. Bizning kodimiz React'ga o'tganda kichraymadi ham, kattalashmadi ham: farqni kutubxona berdi.
Bu raqamlar — shu darsdagi holat. Qism oxirida (effektlar, Context va hooklar qo'shilgach) gzip hajm 83.2 KB bo'ladi — React nima va nega darsidagi raqam shu yakuniy holatniki. Farq 0.1 KB, ya'ni deyarli hamma narsa shu darsda allaqachon to'langan.
7.2 Kod
| v6 | React | |
|---|---|---|
| UI kodi | render.ts 399 + asosiy.ts 746 |
App.tsx 128 + komponentlar 867 + render.tsx 168 + asosiy.ts 541 |
index.html <body> |
92 qator | 7 qator |
| jami | 1 237 | 1 711 |
src/ fayllari |
22 | 43 |
Kutilmagan natija: kod ko'paydi (izohlar bilan sanalgan). Komponentlarga bo'lish fayllar va props turlarini qo'shdi. React kodni qisqartirish uchun emas — u tartib beradi: har bo'lak alohida, "ekran = state dan funksiya", DOM'ni qo'lda yangilash yo'q.
7.3 Tezlik: 1 000 vazifa
Ro'yxatga 1 000 ta vazifa yuklandi va ikki ish o'lchandi: qidiruv (1 000 qatordan 112 qatorga va qaytib) va bitta katakni belgilash. Har ilova 3 marta navbat bilan, har safar 15 o'lchov, natija — mediana:
| O'lchov | v6 | React |
|---|---|---|
| qidiruv, faqat JS | 11.3 / 12.5 / 12.6 ms | 16.4 / 16.8 / 19.0 ms |
| qidiruv, JS + joylashtirish | 33.2 / 34.6 / 43.9 ms | 36.3 / 36.9 / 38.2 ms |
| bitta katak (+ joylashtirish) | 47.2 / 51.5 / 77.3 ms | 18.8 / 19.3 / 20.0 ms |
- v6, 1-o'lchov77,3 ms
- v6, 2-o'lchov51,5 ms
- v6, 3-o'lchov47,2 ms
- React, 1-o'lchov19,3 ms
- React, 2-o'lchov18,8 ms
- React, 3-o'lchov20 ms
Manba: IlmHamroh o'lchovi, 2026-10-12: Chrome 154 headless, i5-12500H, har biri 15 o'lchov medianasi
Ikki xil natija, ikki xil sabab:
- Bitta katak — React 2.4–4 marta tez. v6 da har o'zgarishda butun ro'yxat qayta yasalardi (
replaceChildren— 1 000 ta yangi<li>). React esa yangi va eski elementlarnikeybo'yicha solishtiradi va faqat o'zgargan bitta qatorni yangilaydi. - Qidiruv — React biroz sekinroq (JS qismi 4–6 ms ko'proq). Ko'p qator birdaniga o'zgarganda React'ning solishtirish ishi qo'shimcha xarajat bo'ladi. Joylashtirish (layout) bilan birga esa farq deyarli yo'q.
Ikkala holatda ham vaqt 50 ms dan past (React'da) — foydalanuvchi uchun "darhol". Kompilyator yoki memo ishlatilmagan (React Compiler — 18-qism).
7.4 Lighthouse
Mobil rejim, Lighthouse 13.5.0, ikki o'lchov:
| Performance | Accessibility, Best Practices, SEO | LCP | |
|---|---|---|---|
| v6 | 100 | 100 / 100 / 100 | — |
| React, 1-o'lchov | 97 | 100 / 100 / 100 | 2.6 s |
| React, 2-o'lchov | 98 | 100 / 100 / 100 | 2.1 s |
React versiyasida LCP 2.1–2.6 s, TBT 50–110 ms, CLS 0. Performance ballining 2–3 ga tushishi — 69 KB qo'shimcha JS'ni yuklash va bajarish narxi. Accessibility va boshqalar o'zgarmadi: biz HTML'ni, aria-* atributlarini va fokusni saqlab qoldik.
7.5 Xulosa: nima berdi, nima oldi
| React berdi | React oldi |
|---|---|
| deklarativ UI: "state shunday bo'lsa — ekran shunday" | +69 KB gzip JS |
| bitta qator o'zgarganda tez yangilanish | ko'p qator o'zgarganda biroz sekinroq |
fokus va yozilgan matn o'z-o'zidan saqlanadi (key) |
yangi tushunchalar: render, state, hooklar |
| komponentlarni alohida sinash (Vitest, 22 test) | ko'proq fayl va qator |
Bu narx vazifalar ning TEXNIK-QARZ ida ochiq yozilgan va ongli ravishda qabul qilingan. Kichik statik sahifa uchun React ortiqcha bo'lishi mumkin. Holat ko'p, interfeys murakkab va jamoa katta bo'lsa — tartib narxga arziydi.
8. Vercel'ga deploy
8.1 Vercel nima
Vercel — frontend ilovalarni joylash (hosting) xizmati. GitHub repozitoriyangizni ulaysiz — har push dan keyin Vercel o'zi npm ci va npm run build qiladi, natijani (dist/) dunyo bo'ylab tarqatilgan serverlarga (CDN) joylaydi va manzil beradi: loyiha-nomi.vercel.app. Shaxsiy, notijorat loyihalar uchun bepul (Hobby rejasi). Hozirgacha vazifalar GitHub Pages'da edi (GitHub Pages va Actions). Vercel bunga ikki qulaylik qo'shadi: har PR uchun alohida "preview" manzil va HTTP sarlavhalarini sozlash.
Bu qadamni o'zingiz bajarasiz — o'z GitHub va Vercel hisobingiz bilan. Biz sizning hisobingizga kira olmaymiz, shuning uchun bu bo'limdagi interfeys tavsifi — Vercel'ning rasmiy hujjatidan (vercel.com/docs, 2026-10 holati).
8.2 vercel.json — kanondan
Repo ildizida tayyor fayl:
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"framework": "vite",
"installCommand": "npm ci",
"buildCommand": "npm run build",
"outputDirectory": "dist",
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self' https://ilmhamroh.uz; manifest-src 'self'; worker-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'; require-trusted-types-for 'script'; trusted-types vazifalar"
},
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
}
]
},
{
"source": "/assets/((?!js/|ikonlar/).*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "/sw.js",
"headers": [{ "key": "Cache-Control", "value": "no-cache" }]
}
]
}Kalitma-kalit:
| Kalit | Nima qiladi | Yozilmasa (sukut bo'yicha) |
|---|---|---|
$schema |
muharrirga avtomatik to'ldirish va tekshiruv beradi | faqat muharrir uchun — deploy'ga ta'siri yo'q |
framework, installCommand, buildCommand, outputDirectory |
Vercel nimani qanday build qilishi: npm ci (lock-fayl bo'yicha aniq), npm run build, natija — dist/ |
Vercel loyihadan o'zi aniqlaydi (Vite preset); biz aniq yozdik — taxmin qilmasin |
rewrites |
har qanday manzil index.html ni qaytaradi — SPA uchun; mavjud fayl bo'lsa, fayl ustun |
yo'q — noma'lum manzil 404 |
| CSP | <meta> dagi bilan bir xil + frame-ancestors 'none' (sahifani iframe ga qo'yib bo'lmaydi — buni faqat sarlavha qila oladi) |
sarlavha yo'q, faqat <meta> dagi CSP |
nosniff, Referrer-Policy |
HTTP sarlavhalari | yo'q |
/assets/… |
hash'li fayllar — bir yil, immutable; assets/js/ va assets/ikonlar/ (hash'siz, Rasm va assetlar) — bu qoidaga kirmaydi |
Vercel'ning o'z kesh sarlavhasi |
/sw.js |
Service Worker har safar tekshirilsin — yangi versiya kech qolmasin | Vercel'ning o'z kesh sarlavhasi |
rewrites haqida: vazifalar da hozircha bitta sahifa, lekin filtr URL'da (?filtr=faol). Kelajakda marshrutlar (/vazifa/5) paydo bo'lsa, foydalanuvchi bu manzilni to'g'ridan-to'g'ri ochganda 404 emas, ilova ochiladi (React Router — 19-qism).
Fayl lokal sinalgan: sarlavhalar statik serverda qo'yildi va 159 brauzer tekshiruvi shu sarlavhalar ostida ham o'tdi (159/159). Quyidagi chiqishda har qatorda uch narsa bor: manzil va status, Cache-Control qiymati, CSP sarlavhasi bormi. O'rtadagi ustunga qarang:
/ 200 | no-store | true
/assets/js/tema.js 200 | no-store | true
/assets/ikonlar/ikon.svg 200 | no-store | true
/sw.js 200 | no-cache | true
/assets/index-BzjdvVca.js 200 | public, max-age=31536000, immutable | trueno-store — lokal serverning sukut qiymati (Vercel'da u o'zining sukut sarlavhasini qo'yadi). Muhimi: hash'li fayl — immutable, sw.js — no-cache, hash'siz tema.js va ikonka — uzoq keshlanmaydi.
8.3 Qadamlar (Vercel hujjatidan)
- vercel.com da GitHub hisobi bilan ro'yxatdan o'ting ("Continue with GitHub").
- Add New… → Project ni bosing va
vazifalarrepozitoriyasini tanlang ("Import"). Vercel'ga faqat shu repoga ruxsat berish mumkin — hamma repolaringizga emas. - Sozlamalar sahifasida Framework Preset — "Vite" (avtomatik aniqlanadi). Build va output sozlamalari
vercel.jsondan olinadi — qo'lda o'zgartirmang. - Environment Variables —
vazifalarda kerak emas:VITE_API_URL.env.productionda va u sir emas (brauzerga baribir chiqadi, Vite III: env). Sirni bu yerga ham,VITE_o'zgaruvchiga ham qo'ymang. - Deploy. Bir-ikki daqiqadan keyin manzil chiqadi:
https://<loyiha-nomi>.vercel.app.
Keyingi har git push (asosiy branch'ga) — yangi production deploy. Har PR uchun Vercel alohida preview manzil beradi va PR'ga izoh qilib yozadi — o'zgarishni birlashtirishdan oldin ko'rish uchun.
Terminaldan ham mumkin: Vercel CLI (npx vercel, 2026-10 holatida versiyasi 63.1.2). Lekin u avval vercel login talab qiladi, shuning uchun biz uning chiqishini bu yerda ko'rsatmaymiz. Asosiy yo'l — yuqoridagi GitHub integratsiyasi.
8.4 Deploy'dan keyin tekshiring
- Sahifa ochiladimi, vazifa qo'shiladimi (server — «Bahor» mashq API,
connect-srcCSP'da ruxsat etilgan). - DevTools → Network → hujjat so'rovi → Response Headers:
content-security-policydaframe-ancestors 'none'bormi? assets/index-….jssarlavhasidacache-control: public, max-age=31536000, immutable.- Internetni o'chirib, sahifani yangilang — Service Worker keshdan ochishi kerak (PWA).
- Lighthouse (mobil) — taxminan 97–99 / 100 / 100 / 100.
GitHub Pages workflow'i repoda qoladi — kanon hozircha ikkalasini saqlaydi. Bitta ilovani ikki manzilda doimiy ushlash noqulay: README'dagi "Deploy" bo'limida o'tish tartibi bor (Vercel ishlayotgani tasdiqlangach, Pages workflow'i alohida commit bilan o'chiriladi). Vercel bilan chuqurroq ishlash — domen, muhitlar, cheklovlar — Deploy I: Vercel darsida.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
useState(oqi()) har renderda localStorage o'qiydi |
funksiya chaqirilgan | useState(oqi) — lazy init |
| tahrirda boshqa qator o'chsa, matn yo'qoladi | key={index} |
key={t.id} |
| "N ta qoldi" eskirib qoladi | hisob alohida state | hosilaviy qiymat |
forma reset() dan keyin eski matn |
defaultValue bor |
faqat defaultValue siz reset() |
Vercel'da /vazifa/5 — 404 |
SPA rewrite yo'q | rewrites → /index.html |
Vercel build yiqildi: npm ci lock-fayl xatosi |
package-lock.json commit qilinmagan yoki eskirgan |
lokal npm install, lock-faylni commit qiling |
| yangi versiya chiqmayapti | sw.js keshlangan |
sw.js — no-cache |
10. Mashqlar
Mashqlar kurs/mashqlar/17/24-mini-ilova/ da. 1–3-mashqlar — «Model», «Komponentlar» va «App» bo'limlaridagi ilovani o'zgartirish. O'z Vite loyihangizga ko'chiring yoki natija oynasida «Tahrirlab ko'rish» bilan ishlang.
1-mashq (oson): Raqamlarni o'qing
«Vanilla va React» bo'limidagi jadvallardan:
react-dom-clientbundle'ning necha foizi:- 1 000 vazifada bitta katakni belgilash React'da taxminan necha ms (butun songa yaxlitlang, 2-o'lchov):
index.html<body>React versiyasida necha qator:
Yechim
83.8% — react-dom-client.production.js. 18.8 ms → 19. 7 qator: <main>, <h1>, izoh, <div id="ilova"> va yopuvchi teglar.
2-mashq (o'rta): "Bajarilganlarni tozalash"
Ilovaga Stats yoniga «Bajarilganlarni tozalash» tugmasini qo'shing. U faqat bajarilgan vazifa bo'lsa ko'rinsin (shartli render — hosilaviy), bosilganda bajarilganlar o'chsin. Sof funksiya — clearDone(tasks). Ishora: «Model» bo'limidagi removeTask ga o'xshash filter.
Yechim
Model'ga:
export function clearDone(tasks: readonly Task[]): Task[] {
return tasks.filter((t) => !t.done);
}App da, <Stats> dan keyin:
{tasks.some((t) => t.done) && (
<button onClick={() => setTasks(clearDone(tasks))}>
Bajarilganlarni tozalash
</button>
)}To'liq yechim (yuqoridagi ilova + o'zgarish) mashq papkasida. Sinov: "Non olish" ni belgilang — tugma paydo bo'ladi; bosing — "Onamga qo'ng'iroq" qoladi, hisob "1 ta qoldi, 0 ta bajarildi", tugma yo'qoladi. tasks.some(…) — boolean, shuning uchun 0 && tuzog'i yo'q (JSX II).
3-mashq (qiyin): Muhim vazifalar
Vazifaga important: boolean maydonini qo'shing (TypeScript turi ham). Talablar:
NewTaskFormda «Muhim» checkbox (uncontrolled,FormDatadaget("important") === "on");ViewRowda muhim vazifa oldida★(ekran o'quvchi uchun<span aria-label="muhim">★</span>);- ro'yxat tartibi: avval muhimlar, keyin qolganlari — hosilaviy,
toSortedbilan, state'dagi tartib o'zgarmasin; Statsga "(K tasi muhim)" qo'shilsin, faqat faol muhimlar sanalsin.
Ishora: onAdd imzosi o'zgaradi — (text: string, important: boolean) => string | null; toSorted — immutable yangilash darsidan; boolean'ni songa aylantirib solishtirish: Number(b.important) - Number(a.important).
Yechim
Asosiy o'zgarishlar:
export type Task = {
id: number;
text: string;
done: boolean;
important: boolean;
};
export function sortByImportance(tasks: readonly Task[]): Task[] {
// barqaror saralash: teng muhimlikda asl tartib saqlanadi
return tasks.toSorted(
(a, b) => Number(b.important) - Number(a.important),
);
}
// NewTaskForm ichida
const data = new FormData(form);
const important = data.get("important") === "on";
const problem = onAdd(text, important);
// App ichida
const shown = sortByImportance(visibleTasks(tasks, filter));
// Stats ichida
const urgent = tasks.filter((t) => t.important && !t.done).length;To'liq tekshirilgan yechim mashq papkasida. toSorted yangi massiv qaytaradi — tasks state'dagi tartib (qo'shilish tartibi) o'zgarmaydi, saralash faqat ko'rinishda. Array.prototype.sort (va toSorted) ES2019 dan beri barqaror — teng elementlar tartibini saqlaydi.
4-mashq: Vazifalar qadami
feat/react-ui branch'ida «Vazifalar qadami: butun UI React'da» bo'limidagi o'zgarishlarni kiriting: src/komponentlar/TemaTanlov.tsx, App.tsx ga <TemaTanlov /> va <Izoh />, tema-tanlash.ts (temaniQolla, saqlanganTanlov, tanlovniSaqla, tizimTemasiniKuzat), asosiy.ts, index.html, main.tsx ni o'chirish. Tekshiruvlar, commit, PR.
Yechim
npm run knip—main.tsxo'chgachIzoh.tsxfaqatAppdan import qilinadi; eskitemaniUlaeksporti qolib ketsa, knip "unused export" deydi.- Brauzerda: tema tugmalari sahifa pastida, tanlov sahifa yangilanganda saqlanadi,
<html data-tema>almashadi. JavaScript o'chirilsa — tugmalar umuman yo'q (v6 dahiddenbilan yashirilgan edi). npm run build— JS+CSS jami taxminan 261 KB (gzip ~83 KB);index.htmldanmain.tsxskripti yo'qolgan.
Yakuniy kod — feat: to'liq React UI — tema tanlovi va izoh App ichida commit'i.
5-mashq: Vercel'ga deploy (o'z hisobingiz bilan)
«Vercel'ga deploy» bo'limidagi qadamlarni bajaring va «Deploy'dan keyin tekshiring» ro'yxatini to'ldiring. Natijani README.md ga yozing: Vercel manzili va Lighthouse ballari. Keyin feat/ branch'ida kichik o'zgarish qilib PR oching va Vercel bergan preview manzilni oching.
Yechim
Kutiladigan natija: manzil https://<loyiha>.vercel.app, Response Headers'da CSP (frame-ancestors 'none' bilan), x-content-type-options: nosniff, referrer-policy, hash'li JS'da immutable. Lighthouse mobil — 97–99 / 100 / 100 / 100 atrofida (tarmoq va qurilmaga qarab farq qiladi). PR'da Vercel botining izohi va preview havolasi paydo bo'ladi. Build yiqilsa — Vercel loglarida xato: ko'pincha npm ci (lock-fayl) yoki tsc -b tip xatosi. Lokal npm run build toza bo'lsa, Vercel'da ham toza bo'lishi kerak — Node versiyasi .nvmrc/engines da (Vercel sozlamasida Node 24 tanlanganini tekshiring).
11. Real ishda
- Bu dars — React intervyu topshirig'ining aniq nusxasi. "TODO ilova yozing: qo'shish, o'chirish, filtr, tahrir" — eng ko'p beriladigan uy vazifasi. Baholanadigan narsalar: state tuzilmasi,
key, o'zgarmas yangilash, komponentlarga bo'lish, kirish imkoniyati. - "React'ga o'tamizmi?" — real loyihalarda shunday qaror o'lchov bilan qilinadi: hajm, tezlik, jamoa. Biz qilgan taqqoslash — aynan shu ishning kichik nusxasi.
- Vercel, Netlify, Cloudflare Pages — frontend deploy uchun eng keng tarqalgan xizmatlar. Preview deploy'lar kod ko'rigining odatiy qismi.
- Portfolio:
vazifalar(React, TypeScript, testlar, CSP, PWA) — Vercel manzili bilan rezyumega qo'yiladi.
Xulosa
- Kichik ilova: sof funksiyalar (model) + minimal state (
tasks,filter,editId) + hosilaviy qiymatlar + komponentlar. - Forma: yangi vazifa — uncontrolled +
FormData; tahrir — controlled;key={t.id}tahrirni saqlaydi. vazifalar: tema tanlovi va izohAppichida,index.htmlda faqat<h1>va<div id="ilova">;useState(fn)— lazy init.- O'lchov: JS+CSS 14.3 → 83.1 KB (gzip, 84% —
react-dom), bitta katak 47–77 → 19–20 ms, qidiruv biroz sekinroq, Lighthouse 100 → 97–98. - Vercel:
vercel.json— build, SPA rewrite, CSP va kesh sarlavhalari; deploy — o'z hisobingiz bilan, har PR — preview.
Keyingi dars: Hooklar qoidalari va nega ular aynan shunday — hooklar nega faqat yuqori darajada va shartsiz chaqiriladi: React ularni chaqiruv tartibi bo'yicha qanday eslab qoladi.
Manbalar
- React: "Thinking in React" — react.dev/learn/thinking-in-react
- React:
useState— "Avoiding recreating the initial state" (lazy init) — react.dev/reference/react/useState - Vercel: "Vite on Vercel", "Project configuration (vercel.json)", "Headers", "Rewrites" — vercel.com/docs
- Lighthouse 13.5.0, web-vitals — o'lchovlar 2026-10-12
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!