إزاي تحسّن تطبيقات Next.js بتاعتك عشان تبقى أسرع وأحسن
فهرس المحتويات
Build للأبلكيشن وعمل Analysis
علشان التطبيق بتاعك يكون سريع، لازم توازن بين سرعة التحميل وكفاءة استخدام الموارد وتجربة المستخدم. الهدف إن المستخدم يحس إن التطبيق سلس وسريع من غير أي تأخير أو تقطعات أثناء التصفح.
إزاي تحسّن تطبيقك
أول حاجة لازم تعملها هي تحدد الأماكن اللي محتاجة تحسين. فيه أدوات كتير تساعدك في الموضوع ده:
استخدام npm run build
وتحليل الأداء المتقدم
لما تشغل الأمر ده، Next.js بيعمل نسخة إنتاج للتطبيق وبيطلعلك تفاصيل عن كل صفحة مع معلومات متقدمة عن الأداء:
- الحجم (Size): بيوريك حجم ملفات الـ JavaScript لكل صفحة وتفاصيل الـ chunks.
- First Load JS: بيبينلك كمية الـ JavaScript اللي لازم تتنزّل وتتنفذ قبل ما الصفحة تظهر للمستخدم.
- Lambda Size: حجم الوظائف السيرفرلس في حالة استخدام Serverless Functions.
- Static Pages: الصفحات اللي هيتم بنائها بشكل ثابت أثناء الـ build.
- SSR Pages: الصفحات اللي هتتنفذ على السيرفر.
- ISR Pages: الصفحات اللي هتستخدم Incremental Static Regeneration.
Route (pages) Size First Load JS Lambda
┌ ○ / 5.29 kB 68.3 kB 152 kB
├ └ css/e12f22.css 653 B
├ /_app 0 B 63 kB ---
├ λ /api/data 0 B 63 kB 86 kB
├ ● /products/[id] 3.85 kB 67.2 kB 168 kB
├ ○ /404 3.46 kB 66.5 kB ---
└ ○ /about 3.54 kB 66.5 kB ---
+ First Load JS shared by all 63 kB
├ chunks/framework-2c79e2a64abdb08b.js 45.2 kB
├ chunks/main-0ecb9ccfcb6a9b24.js 15.3 kB
├ chunks/pages/_app-9a0733e01b13add4.js 496 B
└ chunks/webpack-69bfa6990bb9e155.js 1.98 kB
○
: Static page
λ
: API Lambda
●
: ISR Page
استخدام @next/bundle-analyzer
الأداة دي بتساعدك تشوف حجم الـ JavaScript بشكل بصري مع إمكانيات تحليل متقدمة.
خطوات الاستخدام :
- التثبيت:
npm install @next/bundle-analyzer cross-env --save-dev
# أو باستخدام yarn
yarn add @next/bundle-analyzer cross-env --dev
advanced Setup في next.config.js:
// بنستخدم مكتبة تحليل الحزم علشان نشوف حجم الملفات
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true', // بنشغل التحليل لما نحط ANALYZE=true
openAnalyzer: true, // بيفتح صفحة التحليل تلقائي
analyzerMode: 'static', // بيعمل تقرير ثابت
generateStatsFile: true, // بيعمل ملف إحصائيات
statsFilename: 'stats.json', // اسم ملف الإحصائيات
reportFilename: 'analysis-report.html', // اسم ملف التقرير
bundleAnalyzerConfig: {
defaultSizes: 'gzip', // بيقيس الحجم بعد الضغط
excludeAssets: [/node_modules/], // بيستبعد مجلد node_modules
},
});
// بنصدر الإعدادات مع إضافة تحليل الحزم
module.exports = withBundleAnalyzer({
reactStrictMode: true, // بنشغل الوضع الصارم في رياكت
experimental: {
optimizeCss: true, // بنحسن ملفات CSS
optimizeImages: true, // بنحسن الصور
scrollRestoration: true, // بنحفظ مكان السكرول
},
webpack: (config, { isServer }) => {
// لو احنا في جانب العميل (مش السيرفر)
if (!isServer) {
config.optimization.splitChunks = {
chunks: 'all', // بنقسم كل الحزم
minSize: 20000, // أقل حجم للحزمة 20KB
maxSize: 244000, // أقصى حجم للحزمة 244KB
minChunks: 1, // أقل عدد مرات استخدام
maxAsyncRequests: 30, // أقصى عدد طلبات متزامنة
maxInitialRequests: 30, // أقصى عدد طلبات أولية
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/, // بنفصل مكتبات node_modules
priority: -10, // الأولوية
reuseExistingChunk: true, // بنعيد استخدام الحزم الموجودة
},
default: {
minChunks: 2, // لازم يتستخدم مرتين على الأقل
priority: -20, // أولوية أقل
reuseExistingChunk: true, // بنعيد استخدام الحزم الموجودة
},
},
};
}
return config;
},
});
لتشغيل التحليل :
# استخدام cross-env للتوافق بين أنظمة التشغيل المختلفة
npx cross-env ANALYZE=true npm run build
# تحليل مع تتبع مصادر الحزم
npx cross-env ANALYZE=true ANALYZE_SOURCE_MAPS=true npm run build
# تحليل مع تفعيل التحسينات التجريبية
npx cross-env ANALYZE=true EXPERIMENTAL_OPTIMIZATIONS=true npm run build
نتائج التحليل :
عند تشغيل التحليل، سيتم إنشاء الملفات التالية:
analysis-report-client.html: تحليل تفصيلي لحزم الـclient
- حجم كل مكتبة وتأثيرها
- شجرة التبعيات / dependencies tree
- تحليل الـ tree-shaking
- فرص التحسين
analysis-report-server.html: تحليل لحزم الـserver
- أداء الـ SSR
- تحليل الـ memory usage
- تأثير الـ middleware
stats.json: بيانات تفصيلية يمكن استخدامها مع أدوات تحليل أخرى
أدوات تحليل إضافية:
- webpack-bundle-size-analyzer: لتحليل حجم المكتبات
- source-map-explorer: لتتبع مصادر الكود
- lighthouse-ci: لقياس الأداء في بيئة CI/CD
أساليب تحسين الأداء
استخدام Image component في Next.js
Next.js بيوفر مكون Image متطور بيساعد في:
- تحسين الأداء عن طريق lazy loading تلقائي
- تحويل الصور لأحجام مختلفة حسب الشاشة
- استخدام modern image formats زي WebP و AVIF
- تحسين CLS عن طريق حجز مساحة الصورة
import Image from 'next/image'
function Hero() {
return (
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority={true} // للصور المهمة فوق الfold
quality={75} // جودة الصورة (default: 75)
placeholder="blur" // صورة blur لحد ما تحمل
/>
)
}
تحسين تحميل السكريبتات الخارجية بـ Script
component Script بيساعد في:
- التحكم في توقيت تحميل السكريبتات
- تحسين Core Web Vitals
- منع blocking للـ rendering
import Script from 'next/script'
export default function Layout() {
return (
<>
<Script
src="https://analytics.com/script.js"
strategy="lazyOnload" // يتحمل بعد كل حاجة
onLoad={() => console.log('Script loaded')}
/>
<Script
src="https://important-lib.com/script.js"
strategy="beforeInteractive" // يتحمل قبل التفاعل
/>
</>
)
}
إزالة الحزم والمكتبات غير المستخدمة
تقنيات متقدمة لتنظيف المشروع وتحسين الأداء:
- استخدام
depcheck
: أداة بتكشف المكتبات المثبتة ومش مستخدمة في المشروع - تفعيل
tree shaking
: تقنية بتشيل الكود الميت والدوال اللي مش بنستخدمها - استخدام bundle analyzer: بيحلل حجم البنلدز ويساعد في اكتشاف المشاكل
# depcheck بيحلل المشروع ويطلع تقرير بالمكتبات الزيادة
npx depcheck
// تفعيل tree shaking بشكل قوي في ويبباك
module.exports = {
webpack: (config) => {
config.optimization.usedExports = true; // بيفعل تحليل الexports المستخدمة
config.optimization.sideEffects = true; // بيتعامل مع الside effects
return config;
}
}
الـCache و Incremental Static Regeneration (ISR)
استراتيجيات متقدمة للتخزين المؤقت:
- ISR: بيجدد الصفحات الستاتيك تلقائياً كل فترة
- Redis: نظام تخزين مؤقت سريع للبيانات
- stale-while-revalidate: استراتيجية لعرض البيانات القديمة أثناء تحديثها
// مثال لصفحة بتستخدم ISR مع وقت تحديث ديناميكي
export async function getStaticProps() {
const revalidateTime = await getOptimalRevalidateTime() // بنجيب وقت التحديث المناسب
const data = await fetchData() // بنجيب البيانات
return {
props: { data },
revalidate: revalidateTime // بنحدد وقت التحديث
}
}
// مثال لاستخدام Redis للتخزين المؤقت
import Redis from 'ioredis'
const redis = new Redis()
async function getCachedData(key) {
const cached = await redis.get(key) // بنشوف لو البيانات موجودة
if (cached) return JSON.parse(cached) // لو موجودة بنرجعها
const data = await fetchData() // لو مش موجودة بنجيبها
await redis.set(key, JSON.stringify(data), 'EX', 3600) // بنخزنها لمدة ساعة
return data
}
تحسين الخطوط باستخدام next/font
تقنيات متقدمة لتحميل وعرض الخطوط:
- variable fonts: خطوط مرنة بتدعم أوزان مختلفة في ملف واحد
- subsets: بنحمل الحروف اللي محتاجينها بس
- preload: بنحمل الخطوط المهمة مبكراً
import { Inter, Noto_Kufi_Arabic } from 'next/font/google'
const inter = Inter({
subsets: ['latin'], // بنحدد مجموعة الحروف اللاتينية
variable: '--font-inter', // متغير CSS للخط
display: 'swap', // بيعرض خط بديل لحد ما يتحمل
preload: true, // بيحمل الخط مبكراً
fallback: ['system-ui'] // الخطوط البديلة
})
const notoKufi = Noto_Kufi_Arabic({
subsets: ['arabic'], // مجموعة الحروف العربية
variable: '--font-kufi', // متغير CSS للخط العربي
display: 'optional' // يتحمل في الخلفية
})
الـ Lazy Loading وتقسيم الكود
استراتيجيات متقدمة لتحميل المكونات والملفات:
- Dynamic imports: تحميل المكونات عند الحاجة
- Route groups: تنظيم وتقسيم الصفحات
- Module federation: مشاركة الكود بين التطبيقات
// تحميل مكون ثقيل بشكل ديناميكي
const HeavyComponent = dynamic(() => import('./Heavy'), {
loading: () => <Skeleton />, // بنعرض loading مؤقت
ssr: false, // بنوقف Server-side rendering
prefetch: true // بنحمل الكود مقدماً
})
// تنظيم الصفحات في مجموعات
// app/(marketing)/page.tsx // صفحات التسويق
// app/(dashboard)/page.tsx // صفحات لوحة التحكم
// إعداد module federation للمشاريع الكبيرة
const NextFederationPlugin = require('@module-federation/nextjs-mf')
module.exports = {
webpack: (config) => {
config.plugins.push(
new NextFederationPlugin({
name: 'host', // اسم التطبيق الرئيسي
remotes: {
shop: 'shop@http://localhost:3001/remoteEntry.js' // رابط التطبيق الفرعي
}
})
)
return config
}
}
Performance Monitoring
// تفعيل تحليلات Next.js المتقدمة
module.exports = {
experimental: {
// تفعيل تتبع الأداء التفصيلي
instrumentationHook: true,
// تتبع مصادر مقاييس الويب الحيوية
webVitalsAttribution: ['CLS', 'LCP', 'FCP', 'FID', 'TTFB'],
}
}
// دالة لقياس وتتبع مقاييس الأداء في كل صفحة
export function reportWebVitals(metric) {
const { name, value, id, attribution } = metric;
// تتبع جميع المقاييس الحيوية مع معلومات إضافية
analytics.send({
metric_name: name, // اسم المقياس
metric_value: value, // قيمة المقياس
metric_id: id, // معرف فريد للقياس
attribution: attribution, // مصدر ومعلومات إضافية
timestamp: Date.now(), // وقت القياس
page_path: window.location.pathname, // مسار الصفحة
user_agent: navigator.userAgent // معلومات المتصفح
});
// طباعة القياسات في وضع التطوير
if (process.env.NODE_ENV === 'development') {
console.log(`Web Vital: ${name}`, metric);
}
}
إضافات وتحسينات
- استخدام HTTP/3 و QUIC للاتصال السريع
- تفعيل Brotli compression بدل gzip للضغط الأفضل
- استخدام Web Workers للعمليات الثقيلة
- تطبيق Streaming SSR للتحميل التدريجي
- تفعيل Edge Runtime للأداء الأفضل
- استخدام Partial Prerendering للتحميل الذكي
- تطبيق View Transitions API للانتقالات السلسة
الخاتمة
تحسين أداء تطبيقات Next.js عملية مستمرة ومهمة. من خلال تطبيق الممارسات المذكورة، هتقدر تحسن:
- سرعة تحميل الصفحات
- تجربة المستخدم
- ترتيب موقعك في محركات البحث
- استهلاك موارد السيرفر
افتكر إن التحسين مش خطوة واحدة، لكنه رحلة مستمرة مع كل تحديث وإضافة في مشروعك. استخدم الأدوات المتاحة للقياس والتحليل، وطبق التحسينات بشكل تدريجي حسب احتياجات مشروعك.