تطبيقات الويب التقدمية (PWAs)

تطبيق الويب التقدمي (PWA) هو تطبيق ويب يستخدم إمكانات المتصفح الحديثة لتقديم تجربة تشبه التطبيق. تتميز تطبيقات PWA بأنها موثوقة وسريعة وجذابة - وتجمع بين الوصول إلى الويب وإمكانيات تطبيقات الهاتف المحمول الأصلية.
مقدمة
تطبيق الويب التقدمي (PWA) هو تطبيق ويب يستخدم إمكانات المتصفح الحديثة لتقديم تجربة تشبه التطبيق. تتميز تطبيقات PWA بأنها موثوقة وسريعة وجذابة - وتجمع بين الوصول إلى الويب وإمكانيات تطبيقات الهاتف المحمول الأصلية.
تحل تطبيقات PWA توترًا أساسيًا: يريد المستخدمون التوفر الفوري للويب (بدون تثبيت، قابل للربط، محدث دائمًا) ولكنهم يتوقعون أداء وميزات التطبيقات الأصلية (الدعم دون اتصال بالإنترنت، ودفع الإشعارات، ووجود الشاشة الرئيسية). تقدم PWAs كليهما.
الخصائص الأساسية
تمت صياغة مصطلح "تطبيق الويب التقدمي" بواسطة Alex Russell وFrances Berriman من Google في عام 2015. ويجب أن تستوفي PWA هذه الخصائص العشرة:
| # | المبدأ | لماذا يهم؟ |
|---|---|---|
| 1 | تقدمي — يناسب الجميع | تدهور رشيق على المتصفحات القديمة |
| 2 | ** سريع الاستجابة ** — يناسب أي شاشة | سطح المكتب، الجهاز اللوحي، الهاتف المحمول |
| 3 | اتصال مستقل — يعمل دون اتصال بالإنترنت | التخزين المؤقت لعامل الخدمة |
| 4 | يشبه التطبيق — إحساس التطبيق الأصلي | بنية شل، والتنقل السلس |
| 5 | جديد — محدث دائمًا | دورة حياة تحديث عامل الخدمة |
| 6 | آمن — يتم تقديمه عبر HTTPS | يمنع العبث، مطلوب لعمال الخدمة |
| 7 | قابل للاكتشاف — صديق لكبار المسئولين الاقتصاديين | تقوم محركات البحث بفهرسة PWAs |
| 8 | قابلة لإعادة المشاركة — إشعارات الدفع | إعادة جذب المستخدمين مثل التطبيقات الأصلية |
| 9 | قابل للتثبيت — أضف إلى الشاشة الرئيسية | بيان تطبيق الويب |
| 10 | قابل للربط — المشاركة عبر URL | لا يوجد متجر التطبيقات المطلوبة |
الركائز الثلاث لسلطة المياه الفلسطينية
1. عامل الخدمة – المحرك
عامل الخدمة هو ملف JavaScript يتم تشغيله في الخلفية، بشكل منفصل عن صفحة الويب. وهو يعمل بمثابة وكيل شبكة قابل للبرمجة، حيث يعترض الطلبات ويمكّن الوظائف دون اتصال بالإنترنت، ودفع الإشعارات، ومزامنة الخلفية.
دورة حياة عامل الخدمة
Installing → Installed → Activating → Activated → Idle → Terminated
│ │
└─── (new version detected) ──────────────┘
└─── Waiting (until all tabs close) ─────┘
// service-worker.js
const CACHE_NAME = 'my-pwa-v2';
const ASSETS_TO_CACHE = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/offline.html',
'/icons/icon-192.png'
];
// Install event — cache static assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
console.log('Caching app shell');
return cache.addAll(ASSETS_TO_CACHE);
})
);
// Force new service worker to activate immediately
self.skipWaiting();
});
// Activate event — clean old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(name => name !== CACHE_NAME)
.map(name => caches.delete(name))
);
})
);
// Take control of all open pages immediately
clients.claim();
});
// Fetch event — intercept network requests
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
// Return cached response if available
if (cachedResponse) {
return cachedResponse;
}
// Otherwise fetch from network
return fetch(event.request).then(networkResponse => {
// Don't cache non-GET or error responses
if (!event.request.url.includes('/api/')) {
return networkResponse;
}
// Cache API responses for offline use
const responseClone = networkResponse.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, responseClone);
});
return networkResponse;
}).catch(() => {
// Network failed — return offline fallback
if (event.request.mode === 'navigate') {
return caches.match('/offline.html');
}
return new Response('Offline', { status: 503 });
});
})
);
});
استراتيجيات التخزين المؤقت
| استراتيجية | الوصف | حالة الاستخدام |
|---|---|---|
| ** ذاكرة التخزين المؤقت أولا ** | تحقق من ذاكرة التخزين المؤقت، وارجع إلى الشبكة | الأصول الثابتة (CSS، JS، الصور) |
| الشبكة أولا | حاول الشبكة، والعودة إلى ذاكرة التخزين المؤقت | مكالمات API، المحتوى الديناميكي |
| ** قديمة أثناء إعادة التحقق ** | إرجاع ذاكرة التخزين المؤقت والتحديث في الخلفية | خلاصات الأخبار، ومنشورات المدونة |
| الشبكة فقط | جلب دائما من الشبكة | البيانات الحساسة (المصرفية) |
| ** ذاكرة التخزين المؤقت فقط ** | لا يتم الجلب أبدًا من الشبكة | قذيفة التطبيق |
// Stale-while-revalidate strategy
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const fetchPromise = fetch(event.request).then(networkResponse => {
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, networkResponse.clone());
});
return networkResponse;
}).catch(() => cachedResponse);
return cachedResponse || fetchPromise;
})
);
});
2. بيان تطبيق الويب - الهوية
البيان عبارة عن ملف JSON يتحكم في كيفية ظهور PWA عند تثبيته:
{
"name": "My Progressive Web App",
"short_name": "MyPWA",
"description": "An amazing PWA that works offline",
"start_url": "/?source=pwa",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#3367D6",
"orientation": "portrait-primary",
"scope": "/",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"categories": ["productivity", "utilities"],
"lang": "en-US",
"dir": "ltr",
"prefer_related_applications": false
}
وأوضح الخصائص الظاهرة:
العرض: مستقل- يفتح بدون واجهة مستخدم المتصفح (شريط العناوين، علامات التبويب).العرض: ملء الشاشة- ملء الشاشة، بدون متصفح Chrome على الإطلاق.العرض: الحد الأدنى من واجهة المستخدم- الحد الأدنى من عناصر التحكم في المتصفح.النطاق: "/"— ما هي عناوين URL التي تعد جزءًا من PWA.start_url— الصفحة التي يتم فتحها عند تشغيل PWA.- أيقونات قابلة للإخفاء — أيقونات قابلة للتكيف تعمل مع الإخفاء على مستوى نظام التشغيل (مستدير، دائري، وما إلى ذلك).
3. HTTPS – المؤسسة
يحتاج عمال الخدمة إلى سياق آمن. HTTPS إلزامي من أجل:
- تسجيل عمال الخدمة.
- دفع الإخطارات.
- مطالبة التثبيت (قبل حدث التنزيل).
# Redirect HTTP to HTTPS
# Nginx
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
بناء PWA – خطوة بخطوة
1. قم بإنشاء App Shell
غلاف التطبيق هو الحد الأدنى من HTML وCSS وJavaScript المطلوب لعرض واجهة المستخدم. يتم تخزينه مؤقتًا عند التحميل الأول ويعمل كأساس لـ PWA.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#3367D6">
<link rel="manifest" href="/manifest.json">
<link rel="icon" href="/icons/icon-192.png" sizes="192x192">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
<title>My PWA</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header id="app-header">My PWA</header>
<main id="app-content"></main>
<footer id="app-footer">© 2026</footer>
<!-- Register service worker -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(reg => console.log('SW registered:', reg.scope))
.catch(err => console.error('SW registration failed:', err));
});
}
</script>
<script src="/app.js"></script>
</body>
</html>
2. تنفيذ الدعم دون اتصال بالإنترنت
// app.js — Fetch content with offline support
const CACHE_KEY = 'content-cache';
async function loadContent(url) {
try {
const response = await fetch(url);
const data = await response.json();
// Cache for offline
const cache = await caches.open(CACHE_KEY);
cache.put(url, new Response(JSON.stringify(data)));
renderContent(data);
} catch (error) {
// Offline — load from cache
const cached = await caches.match(url);
if (cached) {
const data = await cached.json();
renderContent(data);
showOfflineIndicator();
} else {
showOfflineMessage();
}
}
}
3. أضف إشعارات الدفع
// Request permission
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
// Get push subscription
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicKey)
});
// Send subscription to server
await fetch('/api/push/subscribe', {
method: 'POST',
body: JSON.stringify(subscription)
});
}
}
// service-worker.js — Handle push events
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
body: data.body,
icon: '/icons/icon-192.png',
badge: '/icons/badge-72.png',
vibrate: [100, 50, 100],
data: { url: data.url },
actions: [
{ action: 'open', title: 'Open' },
{ action: 'close', title: 'Dismiss' }
]
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
if (event.action === 'open' || !event.action) {
clients.openWindow(event.notification.data.url);
}
});
PWA مقابل التطبيق الأصلي
| ميزة | PWA | التطبيق الأصلي |
|---|---|---|
| التوزيع | عنوان URL (لا يلزم وجود متجر) | متجر التطبيقات، جوجل بلاي |
| ** تثبيت الاحتكاك ** | نقرة واحدة (موجه المتصفح) | تنزيل وتثبيت والأذونات |
| حجم الملف | كيلو بايت إلى ميغابايت | من 10 ميجابايت إلى 500 ميجابايت+ |
| ** التحديثات ** | فوري (عامل الخدمة) | عبر مراجعة متجر التطبيقات |
| ** غير متصل ** | نعم (عامل الخدمة) | نعم (مدمج) |
| ** دفع الإخطارات ** | نعم | نعم |
| واجهات برمجة التطبيقات الخاصة بالجهاز | الكاميرا، GPS، التسارع، الدفع | الوصول الكامل إلى جميع واجهات برمجة التطبيقات الخاصة بالجهاز |
| بلوتوث | بلوتوث الويب (محدود) | واجهة برمجة تطبيقات بلوتوث كاملة |
| ** مهام الخلفية ** | مزامنة الخلفية (محدودة) | تنفيذ الخلفية الكاملة |
| الأداء | شبه أصلي (JavaScript/WebAssembly) | أصلي (الكود المترجم) |
| ** تحسين محركات البحث ** | قابلة للفهرسة بواسطة محركات البحث | غير قابلة للفهرسة |
| ** تكلفة التطوير ** | قاعدة تعليمات برمجية واحدة | iOS + Android (أو عبر الأنظمة الأساسية) |
| الاحتفاظ بالمستخدمين | أقل (أسهل للمغادرة) | أعلى (مثبت = ملتزم) |
متى تختار PWA على Native
- التطبيقات التي تركز على المحتوى — الأخبار والمدونات والمجلات.
- التجارة الإلكترونية — مواقع التسوق (Pinterest PWA: قضاء وقت إضافي بنسبة 40%).
- تطبيقات المرافق — الآلات الحاسبة والمحولات والطقس.
- التطبيقات منخفضة التفاعل — التطبيقات التي يزورها المستخدمون بشكل غير متكرر.
- الأسواق الناشئة — المستخدمون الذين لديهم مساحة تخزين وبيانات محدودة.
- التحسين التدريجي — تجربة كاملة على الأجهزة الحديثة، وتعمل على الأجهزة القديمة.
عندما يكون التطبيق الأصلي أفضل
- ألعاب عالية الأداء — أكثر من 60 إطارًا في الثانية، وعرض ثلاثي الأبعاد معقد.
- تكامل عميق للأجهزة — الأجهزة الطرفية التي تعمل بتقنية Bluetooth، وNFC، وARKit/ARCore.
- معالجة الخلفية — بث الموسيقى وتتبع اللياقة البدنية.
- ** مطلوب تفاعل كبير من قبل المستخدمين ** — وسائل التواصل الاجتماعي، والرسائل.
معايير الأداء
| متري | قبل سلطة المياه الفلسطينية | بعد سلطة المياه الفلسطينية | تحسين |
|---|---|---|---|
| التحميل الأول | 5s | 1.2 ثانية | أسرع بنسبة 76% |
| الأحمال اللاحقة | 3ث | 0.3 ثانية | أسرع بنسبة 90% |
| ** معدل الارتداد ** | 45% | 20% | تخفيض 55% |
| معدل التحويل | 2% | 3.5% | زيادة 75% |
| الصفحات لكل جلسة | 3 | 5 | زيادة 66% |
نتائج العالم الحقيقي:
| الشركة | المقياس الرئيسي | تحسين |
|---|---|---|
| تويتر (X) لايت | التغريدات المرسلة لكل جلسة | زيادة 75% |
| بينتريست | الوقت الذي يقضيه | زيادة 40% |
| اوبر | إتمام الحجز | سلس حتى على 2G |
| ستاربكس | المستخدمين النشطين يوميا | زيادة 2x |
| علي بابا | معدل التحويل | زيادة 76% |
اختبار وتدقيق PWA
تدقيق Lighthouse PWA
# Run Lighthouse from CLI
npx lighthouse https://example.com --view --preset=desktop
# PWA-specific checks:
# - ✅ Registers a service worker
# - ✅ Responds with 200 when offline
# - ✅ Web app manifest exists
# - ✅ Manifest has display: standalone
# - ✅ Served over HTTPS
# - ✅ Redirects HTTP to HTTPS
# - ✅ Configurable start URL
# - ✅ Icons provided (192px and 512px)
قائمة التحقق من الاختبار اليدوي
## PWA Pre-Flight Checklist
- [ ] HTTPS enabled
- [ ] Service worker registered and active
- [ ] Pages load offline (test with airplane mode)
- [ ] Manifest configured with correct icons
- [ ] "Add to Home Screen" prompt appears
- [ ] PWA opens in standalone mode with no address bar
- [ ] Splash screen displays correctly
- [ ] Push notifications work
- [ ] All pages are responsive (mobile, tablet, desktop)
- [ ] Performance budget met (Lighthouse score > 90)
- [ ] Cross-browser tested (Chrome, Firefox, Safari, Edge)
- [ ] Accessibility audit passes
ميزات PWA المتقدمة
مزامنة الخلفية
// Register a sync event
async function scheduleSync() {
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('sync-pending-data');
}
// Service worker handles sync
self.addEventListener('sync', event => {
if (event.tag === 'sync-pending-data') {
event.waitUntil(syncPendingData());
}
});
مزامنة الخلفية الدورية
// Request periodic sync (requires user engagement)
const registration = await navigator.serviceWorker.ready;
await registration.periodicSync.register('update-content', {
minInterval: 24 * 60 * 60 * 1000 // Once per day
});
واجهة برمجة تطبيقات مشاركة الويب
// Share content using native share sheet
async function shareContent(title, text, url) {
if (navigator.share) {
await navigator.share({ title, text, url });
} else {
// Fallback to custom share UI
copyToClipboard(url);
}
}
واجهة برمجة تطبيقات الشارة
// Set app icon badge
if (navigator.setAppBadge) {
await navigator.setAppBadge(5); // Show "5" on icon
} else if (navigator.clearAppBadge) {
await navigator.clearAppBadge();
}
المزالق المشتركة
| مأزق | مشكلة | الحل |
|---|---|---|
| ** الإفراط في التخزين المؤقت ** | يقدم التطبيق محتوى قديمًا | ذاكرة التخزين المؤقت للإصدار، قم بتنفيذ ذاكرة التخزين المؤقت أولاً لـ Shell، والشبكة أولاً للمحتوى |
| ** لا يوجد احتياطي دون اتصال ** | شاشة بيضاء حاليا | قم دائمًا بتخزين /offline.html برسالة ذات معنى |
| ** فشل خرق ذاكرة التخزين المؤقت ** | عامل الخدمة القديم لا يزال قائما | استخدم SkipWaiting() وclients.claim() |
| ** مشاكل شاشة البداية ** | لون أو رمز المظهر خاطئ | قم بتكوين البيان بشكل صحيح، واستخدم الرموز القابلة للإخفاء |
| ** التوافق مع رحلات السفاري ** | بعض الميزات محدودة (الدفع، المزامنة الدورية) | التحسين التدريجي — التحلل برشاقة |
| حجم ذاكرة التخزين المؤقت كبير | تم تجاوز حصة التخزين (50 ميجابايت لكل أصل) | تقليم ذاكرة التخزين المؤقت القديمة، والحد من حجم ذاكرة التخزين المؤقت |
| start_url مفقود | تفتح الشاشة الرئيسية صفحة خاطئة | حدد دائمًا start_url في البيان |
الاستنتاج
تمثل تطبيقات الويب التقدمية أفضل ما في العالمين - الوصول إلى الويب وإمكانية الوصول إليه بالإضافة إلى الإمكانات وتجربة المستخدم للتطبيقات الأصلية.
متى يتم بناء PWA
- تريد أقصى قدر من الوصول — تطبيق واحد لجميع الأنظمة الأساسية.
- ** يحتاج المستخدم إلى الوصول دون اتصال بالإنترنت ** — التخزين المؤقت لعامل الخدمة.
- تريد التحميل الفوري — بنية غلاف التطبيق.
- تريد إعادة المشاركة — دفع الإشعارات بدون متجر التطبيقات.
- لديك تطبيق يعتمد على المحتوى — الأخبار، والتجارة الإلكترونية، والمرافق.
البدء
- أضف Manifest.json والأيقونات.
- قم بإنشاء عامل خدمة مع إمكانية الرجوع دون اتصال بالإنترنت.
- قم بإجراء تدقيق Lighthouse وأصلح المشكلات الحرجة.
- اختبار على الأجهزة الحقيقية (Android، iOS، سطح المكتب).
- نشر وقياس تفاعل المستخدم.
لا تعد تطبيقات PWA بديلاً للتطبيقات الأصلية في كل السيناريوهات - ولكن بالنسبة لمعظم التطبيقات المستندة إلى المحتوى والتطبيقات المساعدة، فإنها توفر تجربة مستخدم فائقة مع جزء صغير من تكلفة التطوير.
مقالات مختارة

50 مهارة من Claude تستحق البناء في 2026

ابن مساعد ملاحظات خاص بالذكاء الاصطناعي
حوّل مجلد ملاحظات Markdown إلى مساعد بحث دون كشف المسودات الخاصة.

أتمتة استخراج بيانات الفواتير باستخدام OCR
ابن خط معالجة يقرأ الفواتير ويستخرج الحقول ويعلّم القيم غير المؤكدة للمراجعة.
التعليقات
0 تعليقاتلا توجد تعليقات معتمدة بعد. قد تنتظر الردود الجديدة المراجعة.