تطبيقات الويب التقدمية
تطبيقات الويب التقدمية مع Next.js
بناء تطبيقات الويب التقدمية مع Next.js
توفر Next.js دعمًا ممتازًا لتطبيقات الويب التقدمية من خلال إضافة next-pwa. يغطي هذا الدرس كيفية تحويل تطبيق Next.js الخاص بك إلى PWA كامل الميزات مع دعم وضع عدم الاتصال وسلوك يشبه التطبيق.
تثبيت next-pwa
تعتبر إضافة next-pwa الحل الأكثر شعبية وصيانة لإضافة قدرات PWA إلى تطبيقات Next.js.
# تثبيت next-pwa
npm install next-pwa
# أو باستخدام Yarn
yarn add next-pwa
# هيكل المجلدات:
pages/
_app.js
_document.js
index.js
public/
manifest.json
icons/
next.config.js
تكوين next.config.js
قم بتكوين next-pwa في ملف تكوين Next.js الخاص بك مع خيارات مخصصة لإنشاء Service Worker.
// next.config.js
const withPWA = require('next-pwa')({
dest: 'public',
register: true,
skipWaiting: true,
disable: process.env.NODE_ENV === 'development',
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.(?:googleapis|gstatic)\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts',
expiration: {
maxEntries: 4,
maxAgeSeconds: 365 * 24 * 60 * 60 // سنة واحدة
}
}
},
{
urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-font-assets',
expiration: {
maxEntries: 4,
maxAgeSeconds: 7 * 24 * 60 * 60 // 7 أيام
}
}
},
{
urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-image-assets',
expiration: {
maxEntries: 64,
maxAgeSeconds: 24 * 60 * 60 // 24 ساعة
}
}
},
{
urlPattern: /\.(?:js)$/i,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-js-assets',
expiration: {
maxEntries: 32,
maxAgeSeconds: 24 * 60 * 60 // 24 ساعة
}
}
},
{
urlPattern: /\.(?:css|less)$/i,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-style-assets',
expiration: {
maxEntries: 32,
maxAgeSeconds: 24 * 60 * 60 // 24 ساعة
}
}
},
{
urlPattern: /\/api\/.*/i,
handler: 'NetworkFirst',
method: 'GET',
options: {
cacheName: 'apis',
expiration: {
maxEntries: 16,
maxAgeSeconds: 24 * 60 * 60 // 24 ساعة
},
networkTimeoutSeconds: 10
}
}
]
});
module.exports = withPWA({
reactStrictMode: true,
// خيارات تكوين Next.js الأخرى
});
نصيحة احترافية: اضبط
disable: process.env.NODE_ENV === 'development' لمنع تسجيل Service Worker أثناء التطوير، مما يمنع مشاكل التخزين المؤقت.
إنشاء Manifest
أنشئ ملف manifest.json في مجلد public لتحديد البيانات الوصفية لـ PWA الخاص بك.
// public/manifest.json
{
"name": "تطبيق Next.js PWA الخاص بي",
"short_name": "Next PWA",
"description": "تطبيق ويب تقدمي مبني بـ Next.js",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"orientation": "portrait-primary",
"icons": [
{
"src": "/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}
ربط Manifest في _document.js
أضف Manifest ووسوم meta للون الموضوع إلى مكون Document المخصص الخاص بك.
// pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html lang="ar" dir="rtl">
<Head>
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
<meta name="theme-color" content="#000000" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="Next PWA" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
صفحات Fallback في وضع عدم الاتصال
أنشئ صفحات fallback مخصصة لعدم الاتصال تُعرض عندما يفقد المستخدمون الاتصال.
// pages/_offline.js
export default function Offline() {
return (
<div style={{ textAlign: 'center', padding: '50px' }}>
<h1>أنت حاليًا غير متصل بالإنترنت</h1>
<p>يرجى التحقق من اتصالك بالإنترنت والمحاولة مرة أخرى.</p>
<style jsx>{`
div {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
`}</style>
</div>
);
}
// تحديث next.config.js لاستخدام offline fallback
const withPWA = require('next-pwa')({
dest: 'public',
register: true,
skipWaiting: true,
disable: process.env.NODE_ENV === 'development',
fallbacks: {
document: '/_offline',
image: '/static/fallback-image.png'
}
});
Hook لكشف الاتصال/عدم الاتصال
أنشئ Hook مخصص لاكتشاف التغييرات في حالة الشبكة والاستجابة لها.
// hooks/useOnline.js
import { useState, useEffect } from 'react';
export function useOnline() {
const [isOnline, setIsOnline] = useState(
typeof navigator !== 'undefined' ? navigator.onLine : true
);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
// الاستخدام في المكون
import { useOnline } from '../hooks/useOnline';
export default function Home() {
const isOnline = useOnline();
return (
<div>
{!isOnline && (
<div className="offline-banner">
أنت غير متصل بالإنترنت. قد لا تعمل بعض الميزات.
</div>
)}
{/* محتوى الصفحة */}
</div>
);
}
مهم: تطبيقات Next.js PWA تتطلب البناء للإنتاج لاختبار وظائف Service Worker. قم بتشغيل
npm run build && npm start لاختبار ميزات PWA محليًا.
تكوين PWA المتقدم
قم بتكوين خيارات متقدمة للتحكم بشكل أفضل في سلوك Service Worker.
// next.config.js - التكوين المتقدم
const withPWA = require('next-pwa')({
dest: 'public',
register: true,
skipWaiting: true,
disable: process.env.NODE_ENV === 'development',
scope: '/',
sw: 'service-worker.js',
publicExcludes: ['!robots.txt', '!sitemap.xml'],
buildExcludes: [/middleware-manifest\.json$/],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, // 5MB
cacheOnFrontEndNav: true,
aggressiveFrontEndNavCaching: false,
reloadOnOnline: true,
swcMinify: true
});
تمرين:
- قم بتثبيت next-pwa في مشروع Next.js
- قم بتكوين next.config.js مع استراتيجيات التخزين المؤقت في وقت التشغيل
- أنشئ manifest.json مع أيقونات مناسبة
- أضف رابط Manifest إلى _document.js
- أنشئ صفحة fallback لعدم الاتصال
- نفذ useOnline hook
- قم بالبناء واختبر ميزات PWA في وضع الإنتاج
- اختبر وظيفة عدم الاتصال باستخدام Chrome DevTools
نصيحة للأداء: استخدم استراتيجية
NetworkFirst لمسارات API لضمان الحصول على بيانات حديثة عند الاتصال بالإنترنت، ولكن لا تزال توفر استجابات مخزنة مؤقتًا عند عدم الاتصال. استخدم CacheFirst للأصول الثابتة التي نادرًا ما تتغير.