App Router — nima uchun yangi, pages router dan ustunligi

~15 daq · Next.js · Kirish va Asoslar

Muammodan boshlaylik

Siz Pages Router da dashboard yozyapsiz. Dashboard da sidebar bor — barcha sahifalarda ko'rinishi kerak. Siz _app.tsx ga sidebar qo'yasiz. Lekin endi sidebar barcha sahifalarda ko'rinadi — blog, landing page, hamma joyda. Siz faqat dashboard sahifalarida ko'rishni xohlagan edingiz.

Yechim? Har bir dashboard sahifasida if (route.startsWith('/dashboard')) tekshiruvi. Yoki alohida wrapper komponent. Har ikkalasi ham noqulay.

Bu Pages Router ning cheklovi. App Router aynan shu muammoni hal qilish uchun yaratildi.


Pages Router ning 3 ta asosiy muammosi

Muammo 1 — Har bir komponent brauzerda ishlaydi

Pages Router da barcha komponentlar brauzerda render bo'ladi. Bu degani — ma'lumotlar bazasiga to'g'ridan-to'g'ri ulanib bo'lmaydi, maxfiy API kalitlarni komponent ichida ishlatib bo'lmaydi.

Buning uchun alohida getServerSideProps yozish kerak:

// Pages Router — ma'lumot olish va ko'rsatish ikki alohida joy
export async function getServerSideProps() {
  const users = await db.query('SELECT * FROM users')
  return { props: { users } }
}

export default function UsersPage({ users }) {
  return <ul>{users.map(u => <li>{u.name}</li>)}</ul>
}

Kichik loyihada yaxshi ko'rinadi. Lekin 30 ta sahifada 30 ta getServerSideProps — chalkash bo'ladi.

Muammo 2 — Nested layout qiyin

Yuqorida aytgan muammo. Dashboard sahifalari uchun alohida layout qilish — Pages Router da noqulay.

Muammo 3 — Loading va Error holatlari qo'lda

Ma'lumot yuklanayotganda loading spinner ko'rsatish, xato bo'lganda error sahifa ko'rsatish — har bir sahifada qo'lda yoziladi. Takrorlanuvchi kod.


App Router — yechim

Next.js 13 da App Router chiqdi. U yuqoridagi 3 ta muammoni hal qiladi.

1-yechim — Server Components

App Router da komponentlar default holda Server Component. Bu nima degani?

Server Component — serverda ishlaydigan komponent. Brauzerga faqat tayyor HTML yuboriladi, JavaScript yuborilmaydi.

Amalda bu shuni anglatadi: ma'lumotlar bazasiga to'g'ridan-to'g'ri ulanish mumkin, maxfiy kalitlar brauzerga chiqmaydi, async/await to'g'ridan-to'g'ri komponent ichida ishlaydi.

// app/users/page.tsx — Server Component (default)
export default async function UsersPage() {
  // To'g'ridan-to'g'ri DB ga ulanish — bu kod serverda ishlaydi
  const users = await db.query('SELECT * FROM users')

  // Bu kalit brauzerga hech qachon chiqmaydi
  const data = await fetch('https://api.example.com', {
    headers: { 'Authorization': process.env.SECRET_KEY }
  }).then(r => r.json())

  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}

Pages Router dagi 2 ta funksiya — App Router da 1 ta komponent. Ancha toza.

Server Component da nima ishlamaydi?

useState, useEffect, onClick, onChange — bular brauzer API. Server Component da brauzer yo'q, shuning uchun bular ishlamaydi.

Agar bular kerak bo'lsa — faylning boshiga 'use client' yozasiz. Bu komponentni Client Component ga aylantiradi.

'use client'  // ← shu qator yetarli

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}

💡 Qoida: Avval Server Component yozing. Faqat useState, useEffect, event handler kerak bo'lganda 'use client' qo'shing. Sababi — Server Component brauzerga JS yubormasligidan sahifa yengilroq bo'ladi.

2-yechim — Nested Layouts

Yuqoridagi dashboard muammosi App Router da papka tuzilmasi orqali hal bo'ladi:

app/
├── layout.tsx           ← barcha sahifalar uchun (Navbar, Footer)
├── page.tsx             ← /
└── dashboard/
    ├── layout.tsx       ← faqat dashboard sahifalari uchun (Sidebar)
    ├── page.tsx         ← /dashboard
    └── settings/
        └── page.tsx     ← /dashboard/settings

/dashboard/settings ga kirganda sahifa qanday ko'rinadi:

┌──────────────────────────────────┐
│  Navbar  (app/layout.tsx)        │
├─────────┬────────────────────────┤
│         │                        │
│ Sidebar │   Settings sahifasi    │
│(dashb.. │   (settings/page.tsx)  │
│layout)  │                        │
│         │                        │
├─────────┴────────────────────────┤
│  Footer  (app/layout.tsx)        │
└──────────────────────────────────┘

Dashboard layouti faqat dashboard/ papkasi ichidagi sahifalarga qo'llanadi. Blog yoki landing page ga ta'sir qilmaydi.

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className="flex">
      <aside>
        <a href="/dashboard">Bosh sahifa</a>
        <a href="/dashboard/settings">Sozlamalar</a>
      </aside>
      <main>{children}</main>
    </div>
  )
}

Sahifa o'zgarganda faqat {children} yangilanadi — Navbar va Sidebar qayta render bo'lmaydi. Bu performance uchun ham yaxshi.

3-yechim — Maxsus fayllar

App Router da har bir route uchun maxsus fayllar bor. Ular avtomatik ishlaydi — qo'shimcha kod kerak emas.

app/dashboard/
├── page.tsx        ← asosiy kontent
├── loading.tsx     ← ma'lumot yuklanayotganda
├── error.tsx       ← xato bo'lganda
└── not-found.tsx   ← sahifa topilmaganda

loading.tsx — page.tsx ma'lumot yuklab bo'lguncha ko'rsatiladi:

// app/dashboard/loading.tsx
export default function Loading() {
  return (
    <div>
      <p>Yuklanmoqda...</p>
    </div>
  )
}

error.tsx — page.tsx da xato bo'lganda ko'rsatiladi. Bu fayl Client Component bo'lishi shart — chunki reset funksiyasi brauzerda ishlaydi:

// app/dashboard/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error
  reset: () => void
}) {
  return (
    <div>
      <p>Xato: {error.message}</p>
      <button onClick={reset}>Qayta urinish</button>
    </div>
  )
}

reset — sahifani qayta yuklash funksiyasi. Next.js o'zi beradi.


Ikkala router birga ishlatish mumkinmi

Ha. Eski loyihani App Router ga asta-sekin o'tkazayotgan bo'lsangiz — ikkalasi bir vaqtda ishlaydi:

my-app/
├── app/           ← yangi sahifalar (App Router)
│   └── about/
│       └── page.tsx
└── pages/         ← eski sahifalar (Pages Router)
    └── index.tsx

Lekin yangi loyihada faqat App Router ishlatish tavsiya etiladi.


Tez-tez uchraydigan xatolar

1. Server Component da useState ishlatish

Bu eng ko'p uchraydigan xato. Yangi boshlovchilar 'use client' yozishni unutadi.

// ❌ — Server Component da useState bo'lmaydi
export default async function Page() {
  const [open, setOpen] = useState(false)  // xato!
}

Xato xabari: You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client".

// ✅ — 'use client' qo'shing
'use client'

export default function Page() {
  const [open, setOpen] = useState(false)
}

2. error.tsx da 'use client' yo'q

// ❌ — error.tsx da onClick bor, lekin 'use client' yo'q
export default function Error({ reset }) {
  return <button onClick={reset}>Qayta</button>
}

Xato xabari: Event handlers cannot be passed to Client Component props.

// ✅
'use client'

export default function Error({ reset }) {
  return <button onClick={reset}>Qayta</button>
}

3. Ichki layoutda <html><body> yozish

// ❌ — faqat root layout.tsx da kerak
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
  return (
    <html>
      <body>
        <aside>Sidebar</aside>
        {children}
      </body>
    </html>
  )
}

Xato xabari: <html> cannot appear as a child of <body>.

// ✅
export default function DashboardLayout({ children }) {
  return (
    <div className="flex">
      <aside>Sidebar</aside>
      <main>{children}</main>
    </div>
  )
}

Xulosa

App Router — Pages Router ning 3 ta muammosini hal qiladi.

Server Components — komponentlar serverda ishlaydi, async/await to'g'ridan-to'g'ri, brauzerga JS yuborilmaydi. Brauzer API kerak bo'lsa — 'use client'.

Nested Layouts — papka tuzilmasi orqali ichma-ich qoliplar. Dashboard layouti faqat dashboard sahifalariga qo'llanadi.

Maxsus fayllar — loading.tsx, error.tsx avtomatik ishlaydi, qo'shimcha kod kerak emas. error.tsx har doim 'use client'.


Keyingi dars

4-dars: File-based Routing — papka va fayl orqali route yaratish

Dynamic route ([id]), nested route, route group — bularni amalda ko'ramiz.