React bilan real loyiha arxitekturasi — papkalar tuzilishi
Muammodan boshlaylik
Loyiha 3 oy oldin boshlangan. Hozir src/ shunday ko'rinadi:
src/
├── components/ (87 ta fayl)
│ ├── Button.jsx
│ ├── ProductCard.jsx
│ ├── CartItem.jsx
│ ├── AdminUserTable.jsx
│ ├── LoginForm.jsx
│ └── ... 82 ta yana
├── hooks/ (34 ta fayl)
├── utils/ (29 ta fayl)
├── api/ (18 ta fayl)
├── store/ (9 ta fayl)
└── pages/ (22 ta fayl)
Savat funksiyasini o'zgartirish kerak. Qaysi fayllar savatga tegishli? components/ da 87 ta fayl orasidan qidirasiz, keyin hooks/ da, api/ da, store/ da, utils/ da... Besh xil papkada tarqalgan. Savat funksiyasini o'chirish kerak bo'lsa — qaysi fayllarni olib tashlash xavfsiz, hech kim bilmaydi.
Bu — turi bo'yicha guruhlash (by type). Kichik loyihada qulay, katta loyihada — tartibsizlik.
Turi bo'yicha va funksiya bo'yicha
Turi bo'yicha (by type)
src/
├── components/
├── hooks/
├── api/
└── pages/
- ✅ Oddiy, tushunarli — kichik loyihalar (10–20 komponent) uchun yaxshi
- ❌ Bitta funksiya fayllari besh papkada
- ❌ Loyiha o'sgani sari papkalar ulkan bo'ladi
Funksiya bo'yicha (by feature)
src/
├── features/
│ ├── savat/
│ │ ├── components/
│ │ │ ├── SavatQatori.jsx
│ │ │ └── SavatJami.jsx
│ │ ├── hooks/
│ │ │ └── useSavat.js
│ │ ├── api.js
│ │ ├── store.js
│ │ └── index.js
│ ├── katalog/
│ ├── auth/
│ └── buyurtmalar/
├── shared/
│ ├── ui/ // Button, Input, Modal — biznesga bog'liq bo'lmagan
│ ├── hooks/ // useDebounce, useLocalStorage
│ ├── lib/ // formatNarx, sana funksiyalari
│ └── api/ // Axios instance (85-dars)
├── pages/
│ ├── KatalogSahifasi.jsx
│ └── SavatSahifasi.jsx
└── app/
├── App.jsx
├── router.jsx
└── providers.jsx
- ✅ Savat bilan bog'liq hamma narsa — bitta papkada
- ✅ Funksiyani o'chirish — papkani o'chirish
- ✅ Jamoada har kim o'z funksiyasida ishlaydi — kamroq to'qnashuvlar
- ✅ Loyiha o'sganda tuzilma tushunarli qoladi
Redux jamoasi ham, React hamjamiyatining ko'pchiligi ham o'rta va katta loyihalar uchun funksiya bo'yicha tuzilmani tavsiya qiladi (60-dars).
Qatlamlar
Funksiya bo'yicha tuzilmada har bir papkaning o'z vazifasi bor:
| Papka | Nima | Misol |
|---|---|---|
app/ |
Ilovani ishga tushirish: provider lar, router, global stillar | App.jsx, providers.jsx |
pages/ |
Sahifalar — route lar; feature larni yig'adi | SavatSahifasi.jsx |
features/ |
Biznes funksiyalar — foydalanuvchi uchun qiymat | savat/, auth/, katalog/ |
shared/ |
Hech qaysi funksiyaga bog'liq bo'lmagan umumiy kod | ui/Button.jsx, lib/formatNarx.js |
Import qoidasi — bir yo'nalishda
app → pages → features → shared
- Yuqoridagi qatlam pastdagini import qila oladi
- Pastdagi yuqoridagini — yo'q:
shared/hech qachonfeatures/dan import qilmaydi - Feature lar bir-birini to'g'ridan-to'g'ri import qilmaydi (iloji boricha)
// ✅ sahifa feature larni birlashtiradi
// pages/SavatSahifasi.jsx
import { SavatRoyxati } from '@/features/savat';
import { TavsiyaEtilganlar } from '@/features/katalog';
// ❌ feature boshqa feature ning ichiga kirmaydi
// features/savat/components/SavatQatori.jsx
import { MahsulotRasmi } from '@/features/katalog/components/MahsulotRasmi';
Agar ikki feature bir xil narsaga muhtoj bo'lsa — uni shared/ ga ko'chiring yoki sahifada birlashtiring.
[!INFO] Bu g'oyalarning qat'iy, rasmiylashtirilgan versiyasi — Feature-Sliced Design (FSD) metodologiyasi:
app,pages,widgets,features,entities,sharedqatlamlari va aniq import qoidalari bilan. Katta jamoalarda mashhur. Kichik va o'rta loyihalar uchun yuqoridagi soddalashtirilgan versiya ko'pincha yetarli.
Feature ning ochiq API si — index.js
// features/savat/index.js
export { SavatRoyxati } from './components/SavatRoyxati';
export { SavatBelgisi } from './components/SavatBelgisi';
export { useSavat } from './hooks/useSavat';
// Tashqaridan — faqat index orqali
import { SavatBelgisi, useSavat } from '@/features/savat';
index.js — feature ning "eshigi": tashqariga nima chiqishini belgilaydi. Ichki komponentlar (SavatQatori) yashirin qoladi va ularni bemalol o'zgartirish mumkin.
[!WARNING]
shared/ui/index.jskabi juda katta "barrel" fayllar (hamma narsani qayta eksport qiluvchi) muammo tug'dirishi mumkin: bittaButtonimport qilinganda development serveri butun papkani yuklaydi va ba'zan tree shaking yomonlashadi. Barrel larni feature ning ochiq API si uchun ishlating; katta umumiy kutubxonalarda to'g'ridan-to'g'ri import (@/shared/ui/Button) afzal.
Absolute importlar — alias
// ❌ nisbiy yo'llar
import { Button } from '../../../../shared/ui/Button';
// ✅ alias
import { Button } from '@/shared/ui/Button';
// vite.config.js
import { fileURLToPath, URL } from 'node:url';
export default defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
});
// jsconfig.json (yoki tsconfig.json) — muharrir avtomatik to'ldirishi uchun
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@/*": ["src/*"] }
}
}
Fayl boshqa papkaga ko'chirilganda importlar buzilmaydi.
Kichik qoidalar
Nomlash
| Nima | Uslub | Misol |
|---|---|---|
| Komponent fayli | PascalCase | SavatQatori.jsx |
| Hook | use + camelCase |
useSavat.js |
| Util, api | camelCase | formatNarx.js, api.js |
| Papkalar | kebab-case yoki camelCase — izchil | features/savat/ |
Testlar va stillar — yonida
features/savat/components/
├── SavatQatori.jsx
├── SavatQatori.test.jsx
└── SavatQatori.module.css
Komponent o'chirilsa — testi va stili ham birga ketadi.
Bitta faylda nechta komponent?
Asosiy qoida — bitta eksport qilinadigan komponent faylda. Faqat shu faylda ishlatiladigan kichik yordamchi komponentlar — o'sha faylda qolishi mumkin.
Qachon nima tanlash
| Loyiha | Tavsiya |
|---|---|
| O'quv loyihasi, 10–20 komponent | Turi bo'yicha — components/, pages/, hooks/ |
| O'rta loyiha, bir-ikki dasturchi | Funksiya bo'yicha — features/, shared/, pages/ |
| Katta loyiha, jamoa | Funksiya bo'yicha + qat'iy import qoidalari (ESLint) yoki FSD |
[!TIP] Boshidanoq murakkab tuzilma qurmang. Kichik boshlang va loyiha o'sgani sari qayta tuzing: "bu papkada fayllarni topish qiyinlashdi" — qayta tuzish vaqti keldi degan belgi. Import qoidalarini qo'lda kuzatish qiyin —
eslint-plugin-boundariesyokiimport/no-restricted-pathsqoidalari ularni avtomatik tekshiradi.
Nima uchun shunday?
Nima uchun kodni funksiya bo'yicha guruhlash turi bo'yicha guruhlashdan yaxshiroq ishlaydi?
Chunki dasturchi kod bilan vazifalar bo'yicha ishlaydi, fayl turlari bo'yicha emas. "Savatga chegirma qo'shish" vazifasi komponentni, hook ni, API ni va store ni birga o'zgartirishni talab qiladi. Birga o'zgaradigan narsalar birga yashashi kerak — bu dasturlashdagi "cohesion" (bog'liqlik) tamoyili. Turi bo'yicha tuzilma esa birga o'zgaradigan narsalarni ajratib, bir-biriga aloqasi yo'q narsalarni (savat hook i va admin hook i) bitta papkaga qo'yadi. Loyiha kichik bo'lganda bu sezilmaydi; 300 ta faylda esa har bir vazifa "xazina qidirish" ga aylanadi. Yaxshi arxitektura — kodni o'zgartirish oson bo'lgan arxitektura.
Xulosa
Eslab qoling:
- Turi bo'yicha (
components/,hooks/) — kichik loyihalar uchun; o'sganda tartibsizlik - Funksiya bo'yicha —
features/savat/ichida komponentlar, hook lar, api, store birga - Qatlamlar:
app→pages→features→shared; import faqat pastga - Feature lar bir-biriga to'g'ridan-to'g'ri kirmaydi; sahifa ularni birlashtiradi
index.js— feature ning ochiq API si; katta barrel lardan ehtiyot bo'ling- Alias:
@/→src/; testlar va stillar komponent yonida - Kichik boshlang, kerak bo'lganda qayta tuzing; qoidalarni ESLint bilan tekshiring
Keyingi dars
89-dars: React bilan real loyiha — API layer, hooks layer, UI layer