التحميل المتأخر (Lazy Loading)
يساعد التحميل المتأخر في Next.js على تحسين أداء التحميل الأولي للتطبيق عن طريق تقليل كمية JavaScript المطلوبة لعرض مسار.
يسمح لك بتأجيل تحميل مكونات العميل (Client Components) والمكتبات المستوردة، وإدراجها في حزمة العميل فقط عند الحاجة إليها. على سبيل المثال، قد ترغب في تأجيل تحميل نافذة مشروطة حتى ينقر المستخدم لفتحها.
هناك طريقتان لتنفيذ التحميل المتأخر في Next.js:
- استخدام الاستيراد الديناميكي مع
next/dynamic
- استخدام
React.lazy()
مع Suspense
بشكل افتراضي، يتم تقسيم الشفرة (Code Splitting) تلقائيًا لمكونات الخادم (Server Components)، ويمكنك استخدام البث (Streaming) لإرسال أجزاء واجهة المستخدم تدريجيًا من الخادم إلى العميل. ينطبق التحميل المتأخر على مكونات العميل.
next/dynamic
next/dynamic
هو مزيج من React.lazy()
و Suspense. يعمل بنفس الطريقة في كل من دليل app
و pages
للسماح بالهجرة التدريجية.
أمثلة
استيراد مكونات العميل
'use client'
import { useState } from 'react'
import dynamic from 'next/dynamic'
// مكونات العميل:
const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })
export default function ClientComponentExample() {
const [showMore, setShowMore] = useState(false)
return (
<div>
{/* يتم التحميل فورًا، ولكن في حزمة عميل منفصلة */}
<ComponentA />
{/* يتم التحميل عند الطلب، فقط عند تحقيق الشرط */}
{showMore && <ComponentB />}
<button onClick={() => setShowMore(!showMore)}>تبديل</button>
{/* يتم التحميل فقط على جانب العميل */}
<ComponentC />
</div>
)
}
تخطي العرض من جانب الخادم (SSR)
عند استخدام React.lazy()
و Suspense، سيتم عرض مكونات العميل مسبقًا (SSR) بشكل افتراضي.
إذا كنت تريد تعطيل العرض المسبق لمكون عميل، يمكنك استخدام خيار ssr
بقيمة false
:
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })
استيراد مكونات الخادم
إذا قمت باستيراد مكون خادم (Server Component) بشكل ديناميكي، فسيتم تأجيل تحميل مكونات العميل الفرعية فقط - وليس مكون الخادم نفسه.
import dynamic from 'next/dynamic'
// مكون الخادم:
const ServerComponent = dynamic(() => import('../components/ServerComponent'))
export default function ServerComponentExample() {
return (
<div>
<ServerComponent />
</div>
)
}
تحميل المكتبات الخارجية
يمكن تحميل المكتبات الخارجية عند الطلب باستخدام دالة import()
. هذا المثال يستخدم المكتبة الخارجية fuse.js
للبحث الضبابي. يتم تحميل الوحدة فقط على العميل بعد إدخال المستخدم في حقل البحث.
'use client'
import { useState } from 'react'
const names = ['Tim', 'Joe', 'Bel', 'Lee']
export default function Page() {
const [results, setResults] = useState()
return (
<div>
<input
type="text"
placeholder="Search"
onChange={async (e) => {
const { value } = e.currentTarget
// تحميل fuse.js ديناميكيًا
const Fuse = (await import('fuse.js')).default
const fuse = new Fuse(names)
setResults(fuse.search(value))
}}
/>
<pre>Results: {JSON.stringify(results, null, 2)}</pre>
</div>
)
}
إضافة مكون تحميل مخصص
import dynamic from 'next/dynamic'
const WithCustomLoading = dynamic(
() => import('../components/WithCustomLoading'),
{
loading: () => <p>جار التحميل...</p>,
}
)
export default function Page() {
return (
<div>
{/* سيتم عرض مكون التحميل أثناء تحميل <WithCustomLoading/> */}
<WithCustomLoading />
</div>
)
}
استيراد التصديرات المسماة
لاستيراد تصدير مسماة ديناميكيًا، يمكنك إعادته من الـ Promise الذي تُرجعه دالة import()
:
'use client'
export function Hello() {
return <p>مرحبًا!</p>
}
import dynamic from 'next/dynamic'
const ClientComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)