كيفية التحميل المتأخر للمكونات (Client Components) والمكتبات
يساعد التحميل المتأخر في Next.js على تحسين أداء التحميل الأولي للتطبيق عن طريق تقليل كمية JavaScript المطلوبة لعرض مسار.
يسمح لك بتأجيل تحميل المكونات من جانب العميل (Client Components) والمكتبات المستوردة، وتضمينها فقط في حزمة العميل عند الحاجة إليها. على سبيل المثال، قد ترغب في تأجيل تحميل نافذة منبثقة حتى ينقر المستخدم لفتحها.
هناك طريقتان لتنفيذ التحميل المتأخر في Next.js:
- استخدام الاستيراد الديناميكي مع
next/dynamic
- استخدام
React.lazy()
مع Suspense
بشكل افتراضي، يتم تقسيم الكود تلقائيًا للمكونات من جانب الخادم (Server Components)، ويمكنك استخدام البث التدريجي لإرسال أجزاء واجهة المستخدم تدريجيًا من الخادم إلى العميل. ينطبق التحميل المتأخر على المكونات من جانب العميل (Client Components).
next/dynamic
next/dynamic
هو مزيج من React.lazy()
و Suspense. يعمل بنفس الطريقة في دليل app
و pages
للسماح بالهجرة التدريجية.
أمثلة
استيراد مكونات العميل (Client Components)
'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>
)
}
ملاحظة: عندما يقوم مكون الخادم (Server Component) باستيراد مكون عميل (Client Component) بشكل ديناميكي، فإن تقسيم الكود التلقائي غير مدعوم حاليًا.
تخطي SSR
عند استخدام React.lazy()
و Suspense، سيتم التقديم المسبق (SSR) لمكونات العميل افتراضيًا.
ملاحظة: خيار
ssr: false
يعمل فقط مع مكونات العميل، قم بنقله إلى مكونات العميل لضمان عمل تقسيم الكود بشكل صحيح.
إذا كنت تريد تعطيل التقديم المسبق لمكون عميل، يمكنك استخدام خيار ssr
بقيمة false
:
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })
استيراد مكونات الخادم (Server Components)
إذا قمت باستيراد مكون خادم (Server Component) بشكل ديناميكي، فسيتم تأجيل تحميل مكونات العميل التابعة فقط - وليس مكون الخادم نفسه. كما سيساعد في تحميل الأصول الثابتة مسبقًا مثل CSS عند استخدامه في مكونات الخادم.
import dynamic from 'next/dynamic'
// مكون الخادم:
const ServerComponent = dynamic(() => import('../components/ServerComponent'))
export default function ServerComponentExample() {
return (
<div>
<ServerComponent />
</div>
)
}
ملاحظة: خيار
ssr: false
غير مدعوم في مكونات الخادم. سيظهر خطأ إذا حاولت استخدامه في مكونات الخادم.ssr: false
غير مسموح به معnext/dynamic
في مكونات الخادم. يرجى نقله إلى مكون عميل.
تحميل المكتبات الخارجية
يمكن تحميل المكتبات الخارجية عند الطلب باستخدام دالة 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>
)
}
إضافة مكون تحميل مخصص
'use client'
import dynamic from 'next/dynamic'
const WithCustomLoading = dynamic(
() => import('../components/WithCustomLoading'),
{
loading: () => <p>Loading...</p>,
}
)
export default function Page() {
return (
<div>
{/* سيتم عرض مكون التحميل أثناء تحميل <WithCustomLoading/> */}
<WithCustomLoading />
</div>
)
}
استيراد التصديرات المسماة
لاستيراد تصدير مسما بشكل ديناميكي، يمكنك إعادته من Promise الذي تُرجعها دالة import()
:
'use client'
export function Hello() {
return <p>Hello!</p>
}
import dynamic from 'next/dynamic'
const ClientComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)