تطبيقات الويب التقدمية

تطبيقات الويب التقدمية مع React

18 دقيقة الدرس 21 من 30

بناء تطبيقات الويب التقدمية مع React

توفر React دعمًا ممتازًا لبناء تطبيقات الويب التقدمية من خلال قالب PWA في Create React App والأدوات الحديثة. دعنا نستكشف كيفية بناء تطبيقات PWA جاهزة للإنتاج باستخدام React.

قالب PWA في Create React App

يتضمن Create React App قالب PWA مدمج يقوم بإعداد Service Workers وملفات Manifest تلقائيًا.

# إنشاء تطبيق React جديد مع قالب PWA npx create-react-app my-pwa --template cra-template-pwa # أو إضافة PWA لمشروع TypeScript موجود npx create-react-app my-pwa --template cra-template-pwa-typescript # هيكل المشروع يتضمن: src/ service-worker.js serviceWorkerRegistration.js public/ manifest.json icons/

تسجيل Service Worker

يوفر CRA مساعد تسجيل Service Worker يحتاج إلى تفعيله في ملف index.js.

// src/index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import * as serviceWorkerRegistration from './serviceWorkerRegistration'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> ); // تسجيل Service Worker للـ PWA serviceWorkerRegistration.register({ onSuccess: (registration) => { console.log('تم تثبيت PWA بنجاح'); }, onUpdate: (registration) => { // عرض إشعار التحديث للمستخدم if (window.confirm('إصدار جديد متاح! إعادة التحميل للتحديث؟')) { window.location.reload(); } } });
مهم: Service Workers تعمل فقط في بيئة الإنتاج. قم بتشغيل npm run build وقدم مجلد البناء باستخدام خادم ثابت لاختبار ميزات PWA.

Service Worker مخصص مع Workbox

يستخدم Create React App مكتبة Workbox داخليًا. يمكنك تخصيص Service Worker من خلال إنشاء تكوين مخصص.

// src/service-worker.js import { clientsClaim } from 'workbox-core'; import { ExpirationPlugin } from 'workbox-expiration'; import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching'; import { registerRoute } from 'workbox-routing'; import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies'; clientsClaim(); // التخزين المسبق لجميع الأصول المُنشأة من عملية البناء precacheAndRoute(self.__WB_MANIFEST); // تخزين الصور registerRoute( ({ request }) => request.destination === 'image', new CacheFirst({ cacheName: 'images', plugins: [ new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60, // 30 يومًا }), ], }) ); // تخزين طلبات API registerRoute( ({ url }) => url.pathname.startsWith('/api/'), new StaleWhileRevalidate({ cacheName: 'api-cache', plugins: [ new ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 5 * 60, // 5 دقائق }), ], }) ); // توجيه App Shell const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$'); registerRoute( ({ request, url }) => { if (request.mode !== 'navigate') return false; if (url.pathname.startsWith('/_')) return false; if (url.pathname.match(fileExtensionRegexp)) return false; return true; }, createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html') ); self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } });

تكوين Manifest

خصص ملف manifest.json في مجلد public لتحديد مظهر وسلوك تطبيقك.

// public/manifest.json { "short_name": "React PWA", "name": "تطبيق الويب التقدمي بـ React", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" }, { "src": "logo192.png", "type": "image/png", "sizes": "192x192" }, { "src": "logo512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff", "orientation": "portrait-primary", "categories": ["productivity", "utilities"], "description": "تطبيق ويب تقدمي قوي مبني بـ React" }

الدعم في وضع عدم الاتصال في React

قم بتنفيذ كشف الاتصال بالإنترنت وتقديم تعليقات للمستخدمين عند فقدان الاتصال.

// src/hooks/useOnlineStatus.js import { useState, useEffect } from 'react'; export function useOnlineStatus() { const [isOnline, setIsOnline] = useState(navigator.onLine); useEffect(() => { function handleOnline() { setIsOnline(true); } function handleOffline() { setIsOnline(false); } window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); }; }, []); return isOnline; } // الاستخدام في المكون import { useOnlineStatus } from './hooks/useOnlineStatus'; function App() { const isOnline = useOnlineStatus(); return ( <div> {!isOnline && ( <div className="offline-banner"> أنت حاليًا غير متصل بالإنترنت. قد تكون بعض الميزات محدودة. </div> )} <!-- بقية التطبيق --> </div> ); }
نصيحة احترافية: استخدم مكتبات React Query أو SWR للتخزين المؤقت للـ API بما يتوافق بسلاسة مع استراتيجيات التخزين المؤقت لـ Service Worker.

مكون كشف التحديثات

أنشئ مكونًا للتعامل مع تحديثات Service Worker بشكل سلس.

// src/components/UpdateNotification.js import { useState, useEffect } from 'react'; import * as serviceWorkerRegistration from '../serviceWorkerRegistration'; function UpdateNotification() { const [showUpdate, setShowUpdate] = useState(false); const [registration, setRegistration] = useState(null); useEffect(() => { serviceWorkerRegistration.register({ onUpdate: (reg) => { setShowUpdate(true); setRegistration(reg); } }); }, []); const updateApp = () => { if (registration && registration.waiting) { registration.waiting.postMessage({ type: 'SKIP_WAITING' }); window.location.reload(); } }; if (!showUpdate) return null; return ( <div className="update-notification"> <p>إصدار جديد متاح!</p> <button onClick={updateApp}>التحديث الآن</button> </div> ); } export default UpdateNotification;
تمرين:
  1. أنشئ تطبيق React PWA جديد باستخدام قالب CRA
  2. خصص manifest.json بتفاصيل تطبيقك
  3. أضف useOnlineStatus hook إلى تطبيقك
  4. نفذ مكون UpdateNotification
  5. قم بالبناء واختبر وظيفة عدم الاتصال باستخدام Chrome DevTools
  6. أضف تخزين مخصص لطلبات API باستخدام Workbox
اختبار ميزات PWA: اختبر دائمًا في وضع الإنتاج. قم بتشغيل npm run build، ثم قدم باستخدام: npx serve -s build. افتح Chrome DevTools ← علامة تبويب Application لفحص Service Workers والتخزين المؤقت وManifest.