Frontend Architecture
Frontend arxitekturasi nima, Layer-based va Feature-based tuzilma farqi, unidirectional data flow, komponent chegaralari (coupling/cohesion), rendering strategiyasini tanlash (CSR/SSR/SSG/ISR), code splitting arxitekturasi, micro-frontends, va arxitektura qarorlarining V8/brauzer darajasida bundle parse/compile va main thread'ga ta'siri.
95-dars
Frontend Architecture
Kichik loyihada arxitektura haqida o'ylash shart emas — 5 ta komponent, 2 ta sahifa, hamma narsa ko'z oldingizda. Lekin loyiha o'sib, 50, keyin 500 komponentga yetganda, savol o'zgaradi: "qanday yozaman" emas, balki "bu yangi kod qayerga tushishi kerak, va u qolgan hamma narsaga qanday ta'sir qiladi".
Frontend arxitekturasi — bu kodni "ishlaydigan" holatdan "o'zgartirish oson" holatga o'tkazadigan qarorlar to'plami: fayllar qayerda yotadi, data qaysi yo'nalishda oqadi, komponentlar bir-biriga qanday bog'langan. Bu — texnologiya emas, balki chegaralar chizish intizomi.
1. Nega tuzilma o'zi muhim — "spagetti" muammosi
Arxitekturasiz loyihada odatiy holat:
// UserProfile.jsx — hammasi bitta joyda
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/user').then(r => r.json()).then(setUser);
}, []);
const formatDate = (d) => new Date(d).toLocaleDateString('uz-UZ');
const validateEmail = (e) => /\S+@\S+\.\S+/.test(e);
// ... 300 qator JSX, validatsiya, fetch, formatlash — hammasi aralash
}
Muammo hajmda emas — muammo shundaki, bu komponentni sinash, qayta ishlatish yoki tushunish qiyin, chunki u bir vaqtning o'zida to'rtta ishni qiladi: data olish, validatsiya, formatlash, render qilish. Har birini o'zgartirsangiz, boshqasini buzib qo'yish xavfi bor.
Arxitektura — bu ayni shu vazifalarni ataylab ajratish:
UserProfile/
index.jsx ← faqat render
useUser.js ← faqat data olish (custom hook)
formatDate.js ← faqat formatlash (sof funksiya)
validateEmail.js ← faqat validatsiya (sof funksiya)
Endi har bir fayl — bitta sabab bilan o'zgaradi (Single Responsibility). formatDateni test qilish uchun React'ni ishga tushirish shart emas.
2. Layer-based vs Feature-based tuzilma
Loyiha papkalarini tashkil qilishning ikki asosiy yo'li bor:
Layer-based (texnik qatlam bo'yicha): Feature-based (biznes funksiya bo'yicha):
src/ src/
components/ features/
UserCard.jsx user-profile/
ProductCard.jsx UserCard.jsx
hooks/ useUser.js
useUser.js product-list/
useProducts.js ProductCard.jsx
utils/ useProducts.js
formatDate.js formatPrice.js
formatPrice.js
| Layer-based | Feature-based | |
|---|---|---|
| Yangi funksiya qo'shish | 4-5 ta papkaga bir nechta fayl qo'shiladi | Bitta yangi papka, hammasi bir joyda |
| Funksiyani o'chirish | Fayllarni har joydan qidirib topish kerak | Bitta papkani o'chirish yetarli |
| Kichik loyiha uchun | Qulay — kam fayl, tez topiladi | Ortiqcha struktura |
| Katta loyiha (50+ komponent) uchun | "Qayerda nima bor" qidirish qiyinlashadi | Yaxshi masshtablanadi — jamoalar mustaqil ishlaydi |
Amalda: kichik loyihalar layer-based bilan boshlaydi, o'sgach feature-based (yoki Feature-Sliced Design kabi rasmiy metodologiya) tomon siljiydi — chunki bitta funksiya ustida ishlayotganda, unga tegishli hamma narsa bir joyda bo'lishi kognitiv yukni kamaytiradi.
3. Data oqimi — nega yo'nalish muhim
Katta ilovalarda eng ko'p uchraydigan xato — ikki tomonlama, tartibsiz data bog'lanishi:
// XATARLI: ComponentA state'ni o'zgartiradi, ComponentB ham xuddi shu state'ga
// to'g'ridan-to'g'ri tegadi — kim, qachon o'zgartirganini kuzatib bo'lmaydi
Zamonaviy arxitektura unidirectional data flow (bir yo'nalishli data oqimi) printsipiga tayanadi:
State (bitta manba)
│
▼
UI (state asosida render qilinadi)
│
▼
Event (foydalanuvchi harakati — click, input)
│
▼
Action / State yangilanishi
│
└──────► (yana State'ga qaytadi, doira yopiladi)
Bu — Redux, Zustand, hatto oddiy useState + props pattern'ning barchasida bir xil g'oya: data faqat bitta yo'nalishda oqadi, shuning uchun har qanday o'zgarish qayerdan kelganini aniq kuzatish mumkin. Bu — DOM va Virtual DOM darsida ko'rgan "eski vs yangi" solishtirish g'oyasining architectural darajadagi davomi: agar state qayerdan o'zgarganini bilmasangiz, nimani solishtirish kerakligini ham bilmaysiz.
State qayerda yashashi kerak — "state joylashuvi" qoidasi
| State turi | Qayerda saqlanadi | Misol |
|---|---|---|
| Faqat bitta komponentga tegishli | Local state (useState) |
Modal ochiq/yopiqligi |
| Bir nechta "qarindosh" komponentga kerak | Lifted state (ota komponentda) | Forma qiymati va uning preview'i |
| Butun ilovaga kerak | Global state (Context, Redux, Zustand) | Login qilgan foydalanuvchi, til |
| Server'dan kelgan, keshlanadigan | Server state (React Query, SWR) | API'dan olingan mahsulotlar ro'yxati |
Eng ko'p uchraydigan arxitektura xatosi — hamma narsani global state'ga tiqish. Bu — har bir kichik o'zgarishda butun ilovani qayta render qilishga (yoki keraksiz murakkablikka) olib keladi. Qoida: state'ni iloji boricha pastda, kerak bo'lganda yuqoriga ko'taring ("lift state up" faqat zarurat tug'ilganda).
4. Komponent chegaralari — Coupling va Cohesion
Ikkita asosiy tushuncha arxitektura sifatini o'lchaydi:
- Cohesion (bog'liqlik ichkarida) — bitta modul ichidagi narsalar qanchalik mantiqan birga turadi. Yuqori cohesion — yaxshi (
useUser.jsfaqat user bilan ishlaydi). - Coupling (bog'liqlik tashqarida) — modullar bir-biriga qanchalik qattiq bog'langan. Past coupling — yaxshi (bitta modulni o'zgartirish boshqasini buzmasligi kerak).
// YUQORI COUPLING — yomon: ProductList to'g'ridan-to'g'ri Cart ichki tuzilmasini biladi
function ProductList({ cart }) {
return <button onClick={() => cart.items.push(product)}>Qo'shish</button>;
}
// PAST COUPLING — yaxshi: ProductList faqat "nima kerakligini" e'lon qiladi,
// QANDAY bajarilishini bilmaydi
function ProductList({ onAddToCart }) {
return <button onClick={() => onAddToCart(product)}>Qo'shish</button>;
}
Bu — Web Components darsida ko'rgan CustomEvent + composed: true g'oyasi bilan bir xil printsip: komponent o'z ichki holatini tashqariga oshkor qilmasdan, faqat "signal chiqaradi", tashqarisi qanday reaksiya berishni o'zi hal qiladi.
5. Rendering strategiyasini tanlash — arxitektura qarori
SSR vs CSR va Hydration darslarida ko'rgan tushunchalar — bu shunchaki texnik detal emas, balki arxitekturaning eng boshida qabul qilinadigan qaror, chunki uni loyihaning yarmisida o'zgartirish qimmatga tushadi:
| Strategiya | Qachon tanlanadi |
|---|---|
| CSR (Client-Side Rendering) | Admin panel, dashboard — SEO kerak emas, tez ichki navigatsiya muhim |
| SSR (Server-Side Rendering) | Blog, marketing sayt — SEO va tezkor birinchi ko'rinish muhim |
| SSG (Static Site Generation) | Kontent kam o'zgaradigan sahifalar (dokumentatsiya, landing) — build vaqtida tayyorlanadi, server yuki yo'q |
| ISR (Incremental Static Regeneration) | SSG + vaqti-vaqti bilan yangilanish kerak bo'lgan sahifalar (masalan, mahsulot narxi) |
Bu qaror — sahifa darajasida ham qabul qilinishi mumkin (Next.js kabi framework'larda har bir route o'zining strategiyasini tanlashi mumkin): masalan, /blog/* — SSG, /dashboard/* — CSR, / — SSR. Bu — hybrid arxitektura, va aynan shu moslashuvchanlik zamonaviy meta-framework'larning asosiy qadriyati.
6. Code Splitting arxitekturasi — bundle chegaralari
Performance Budget darsida ko'rganimizdek, JS — eng "og'ir" resurs turi. Arxitektura darajasida bu shuni anglatadi: qaysi kod qachon yuklanishi kerakligini oldindan rejalashtirish:
// Route-based splitting — har bir sahifa alohida bundle
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
// Component-based splitting — kam ishlatiladigan, og'ir komponent uchun
const VideoEditor = lazy(() => import('./features/VideoEditor'));
Yaxshi arxitektura — foydalanuvchi hech qachon ishlatmasligi mumkin bo'lgan kodni (masalan, admin-only modal, murakkab chart kutubxonasi) boshlang'ich bundle'ga qo'shmaydi. Bu qaror papka tuzilmasi bilan chambarchas bog'liq: feature-based struktura, har bir features/* papkasini alohida chunk qilib bo'lish tabiiy ravishda osonlashtiradi.
7. Micro-frontends — jamoalar ko'payganda
Bitta katta ilova ustida 5-10 ta jamoa mustaqil ishlaganda, bitta umumiy kodbaza (monolith frontend) "hamma hammaga to'sqinlik qiladi" muammosiga olib keladi: bitta jamoaning deploy'i boshqasining ishini kutishi kerak bo'ladi.
Micro-frontend arxitekturasi — ilovani mustaqil deploy qilinadigan qismlarga bo'ladi, har biri o'z jamoasi, hatto o'z framework'i (React, Vue) bilan ishlashi mumkin:
shell-app (asosiy konteyner)
├── checkout-app (Jamoa A, alohida deploy)
├── product-catalog (Jamoa B, alohida deploy)
└── user-profile (Jamoa C, alohida deploy)
Bu yerda Shadow DOM darsida ko'rgan izolyatsiya g'oyasi amaliy ahamiyat kasb etadi: agar har bir micro-frontend o'z CSS/JS'ini olib kelsa, ular bir-birining stillariga "aralashib ketmasligi" uchun aynan Shadow DOM kabi native izolyatsiya vositalari ishlatiladi (yoki Module Federation kabi build-time yechimlar).
Muhim: micro-frontends — bu "har doim yaxshi" yechim emas, balki muammoga javob: agar sizda bitta kichik jamoa bo'lsa, bu ortiqcha murakkablik qo'shadi. Bu faqat tashkiliy shkala (ko'p mustaqil jamoa) muammosini yechadi, texnik muammoni emas.
8. Xavfsizlik va performance — arxitekturaga singib ketgan qarorlar
XSS darsida ko'rganimizdek, innerHTML yoki dangerouslySetInnerHTML — bu bitta joyda xato qilinadigan narsa emas, balki arxitektura darajasida oldini olinadigan narsa: agar loyihada "foydalanuvchi kiritgan HTML ko'rsatiladigan barcha joylar — faqat bitta <SafeHtml> komponenti orqali o'tadi" degan qoida bo'lsa, DOMPurify'ni har safar qo'lda chaqirish unutilmaydi, chunki tekshirish bitta markazlashgan joyda.
Xuddi shunday, Performance Budget — bu CI'da tekshiriladigan raqam bo'lishidan oldin, arxitektura qarori: yangi kutubxona qo'shishdan oldin "bu bizning bundle budget'imizga sig'adimi" degan savolni loyihalash bosqichida berish, keyin CI'da ushlab qolishdan ancha arzon.
9. V8 va brauzer darajasida — arxitektura qarorlari qanday moddiylashadi
Bu yerda muhim tushuncha bor: arxitektura o'zi V8'da "ishlamaydi" — u shunchaki kod tuzilishi. Lekin har bir arxitektura qarori, oxir-oqibat, V8 va brauzerning aniq, o'lchanadigan xarajatlariga aylanadi:
Feature-based struktura + Code splitting
↓ natijada
Kichikroq boshlang'ich bundle
↓ natijada
V8 kamroq baytni Parse + Compile qiladi (Ignition bosqichi qisqaradi)
↓ natijada
Main Thread tezroq bo'shaydi
↓ natijada
LCP / INP yaxshilanadi (foydalanuvchi HIS QILADIGAN natija)
Va aksincha: yomon arxitektura (hamma narsa bitta katta bundle'da, hamma narsa global state'da, har bir o'zgarish butun daraxtni qayta render qiladi) — bu to'g'ridan-to'g'ri ko'proq createElement/appendChild chaqiruvi, ko'proq reflow, ko'proq Long Task degani (Performance Budget darsida ko'rilgan 50ms chegara).
Ya'ni frontend arxitekturasi — bu shu kursda o'rgangan barcha past darajadagi mexanizmlarni (V8 Parse/Compile, DOM reflow/repaint, Event Loop, Virtual DOM diffing) loyihaning butun umri davomida boshqarib turish uchun ustiga qo'yiladigan inson qatlami.
10. Amaliy checklist — yangi loyiha boshlaganda so'raladigan savollar
- Tuzilma: Layer-based yetarlimi, yoki feature-based kerakmi (jamoa hajmi, loyiha o'sish tezligiga qarab)?
- State: Qaysi state global, qaysi lokal bo'lishi kerak — hammasini bitta joyga tiqmaslik.
- Rendering: Har bir sahifa uchun CSR/SSR/SSG/ISR'dan qaysi biri mos — SEO va tezlik talablariga qarab.
- Bundle chegaralari: Qaysi kod "darhol kerak", qaysi kod
lazy()orqali kechiktirilishi mumkin. - Xavfsizlik: Foydalanuvchi data'si ko'rsatiladigan joylar markazlashganmi, yoki har joyda alohida-alohida yozilganmi?
- Masshtablash: Agar jamoa 3 martaga ko'paysa, joriy tuzilma baribir ishlay oladimi, yoki micro-frontend kerak bo'ladimi?
Arxitektura — bir marta qabul qilinadigan qaror emas, balki loyiha o'sgani sayin qayta ko'rib chiqiladigan, doimiy jarayon.