Dynamic Import — komponentlarni bo'laklash

~15 daq · Next.js · Performance va Optimizatsiya

Muammodan boshlaylik

Mahsulot sahifasi:

  • Narx tarixi grafigi (recharts — ~200 KB) — faqat "Grafik" tabi ochilganda kerak
  • Xarita (leaflet — ~150 KB) — sahifaning eng pastida
  • Rasm tahrirlagich — faqat sotuvchi "Tahrirlash" ni bosganda
  • Chat vidjeti — foydalanuvchilarning 3% i ishlatadi

Hammasi birinchi yuklanishda keladi. Foydalanuvchilarning 90% i ularni hech qachon ochmaydi, lekin hammasi kutadi.


Hayotiy o'xshatma

Sayohatga butun uyni olib ketmaysiz. Kerakli narsalar — chamadonda (asosiy bundle). Qolganlari — uyda: kerak bo'lsa, pochta orqali yuborishadi (dynamic import). Chamadon yengil, yo'lga tez chiqasiz.


next/dynamic

'use client'

import dynamic from 'next/dynamic'
import { useState } from 'react'

const NarxGrafigi = dynamic(() => import('@/components/NarxGrafigi'), {
  loading: () => <div className="h-64 animate-pulse rounded-lg bg-gray-100" />,
})

export function MahsulotTablari({ mahsulotId }: { mahsulotId: string }) {
  const [tab, setTab] = useState<'tavsif' | 'grafik'>('tavsif')

  return (
    <div>
      <button onClick={() => setTab('tavsif')}>Tavsif</button>
      <button onClick={() => setTab('grafik')}>Narx tarixi</button>

      {tab === 'grafik' && <NarxGrafigi mahsulotId={mahsulotId} />}
    </div>
  )
}
  • NarxGrafigi va recharts — alohida JavaScript fayl (chunk)
  • U faqat komponent birinchi marta render qilinganda yuklanadi
  • loading — yuklanayotganda ko'rsatiladigan joy saqlovchi (CLS ga qarshi — aynan o'lchamda)

SSR ni o'chirish

Faqat brauzerda ishlaydigan kutubxonalar (window, document ga murojaat qiladiganlar — xaritalar, ba'zi tahrirlagichlar):

'use client'

import dynamic from 'next/dynamic'

const Xarita = dynamic(() => import('@/components/Xarita'), {
  ssr: false,
  loading: () => <div className="h-80 bg-gray-100" />,
})

[!WARNING] ssr: false faqat Client Component ichida ishlatiladi. Server Component da dynamic(..., { ssr: false }) — build xatosi. Uni alohida 'use client' faylga o'rab, o'sha komponentni Server Component dan import qiling.

// components/XaritaClient.tsx
'use client'
import dynamic from 'next/dynamic'
export const XaritaClient = dynamic(() => import('./Xarita'), { ssr: false })
// app/dokonlar/page.tsx (Server Component)
import { XaritaClient } from '@/components/XaritaClient'
export default function Page() {
  return <XaritaClient />
}

Nomlangan export

const Grafik = dynamic(() => import('recharts-wrapper').then((m) => m.LineGrafik))

import() modul obyektini qaytaradi — kerakli export ni .then bilan oling.


Ko'rinishga kelganda yuklash

Sahifa pastidagi og'ir komponent — foydalanuvchi u yerga scroll qilganda:

'use client'

import dynamic from 'next/dynamic'
import { useEffect, useRef, useState } from 'react'

const Xarita = dynamic(() => import('./Xarita'), { ssr: false })

export function KechXarita() {
  const ref = useRef<HTMLDivElement>(null)
  const [korindi, setKorindi] = useState(false)

  useEffect(() => {
    const kuzatuvchi = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) {
        setKorindi(true)
        kuzatuvchi.disconnect()
      }
    }, { rootMargin: '200px' })
    if (ref.current) kuzatuvchi.observe(ref.current)
    return () => kuzatuvchi.disconnect()
  }, [])

  return <div ref={ref} className="h-80">{korindi && <Xarita />}</div>
}

rootMargin: '200px' — ekranga yetishidan biroz oldin yuklashni boshlash.


Hodisada kutubxona yuklash

Komponent emas, oddiy kutubxona kerak bo'lsa:

'use client'

export function EksportTugmasi({ malumot }: { malumot: unknown[] }) {
  async function eksport() {
    const XLSX = await import('xlsx')                 // faqat bosilganda
    const varaq = XLSX.utils.json_to_sheet(malumot)
    const kitob = XLSX.utils.book_new()
    XLSX.utils.book_append_sheet(kitob, varaq, 'Hisobot')
    XLSX.writeFile(kitob, 'hisobot.xlsx')
  }

  return <button onClick={eksport}>Excel ga yuklab olish</button>
}

Oldindan yuklash

Foydalanuvchi tugma ustiga kelganda — bosishidan oldin yuklashni boshlash:

<button onMouseEnter={() => import('xlsx')} onClick={eksport}>

Server Component lar va bo'laklash

// Server Component — dynamic kerak emas
import { OgirServerKomponent } from './OgirServerKomponent'

Server Component lar brauzerga JavaScript sifatida yuborilmaydi — ularni "bo'laklash" kerak emas. App Router sahifalar bo'yicha kodni avtomatik bo'ladi: har bir route faqat o'z kodini yuklaydi. dynamic — Client Component lar ichidagi og'ir qismlar uchun.

[!TIP] Nimani dinamik qilish kerak: birinchi ekranda ko'rinmaydigan, kam ishlatiladigan va og'ir (50 KB+) komponentlar. Kichik komponentlarni dinamik qilish — foydadan ko'ra ko'proq so'rov va kechikish.


Tez-tez uchraydigan xatolar

1. Birinchi ekrandagi asosiy kontentni dinamik qilish

Hero bo'lim dynamic — LCP yomonlashadi: avval asosiy bundle, keyin yana bitta so'rov.

2. loading siz

Komponent yuklanganda kontent "sakraydi" (CLS). Aynan o'lchamdagi joy saqlovchi.

3. Server Component da ssr: false

Build xatosi — client wrapper orqali.


Xulosa

dynamic(() => import('./X'), { loading }) — alohida chunk, kerak bo'lganda yuklanadi
ssr: false — faqat brauzer kutubxonalari; faqat Client Component ichida
Nomlangan export — .then(m => m.Nom)
IntersectionObserver — ko'rinishga kelganda yuklash
await import('lib') — hodisada kutubxona; onMouseEnter — oldindan yuklash
Server Component — bo'laklash kerak emas; route lar avtomatik bo'linadi
Faqat og'ir, kam ishlatiladigan, birinchi ekrandan tashqaridagilarni

Keyingi dars

57-dars: Image optimizatsiya — chuqurroq, sizes, priority

next/image ning nozik sozlamalari — har bir qurilmaga to'g'ri o'lchamdagi rasm.