سأشرح في هذا الموضوع طريقة تحويل موقع ويب إلى تطبيق باستخدام تقنية Progressive Web App (PWA).
سيكون الشرح عمليًا بالكامل في شكل ورشة مفتوحة لتبادل الخبرات وتقديم المساعدة الميدانية والنصائح التقنية.
النتيجة: تطبيق ويب يعمل كتطبيق أصلي على الهاتف وسطح المكتب، مع إمكانية التثبيت مباشرة من المتصفح، وكذلك النشر على المتاجر Google Play وApple App Store وMicrosoft Store.
سنعتمد فقط على JavaScript دون الحاجة إلى أي مكتبات أو إضافات خارجية.
الشرح سيكون خطوة بخطوة، مع أمثلة عملية وأكواد قابلة للنسخ، وفي النهاية سنقدّم إرشادات حول تعبئة التطبيق للنشر على مختلف المتاجر.
ما هي تقنية Progressive Web App (PWA)
تقنية PWA هي طريقة لتحويل أي موقع ويب عادي إلى تجرِبة تشبه التطبيقات الأصلية Native Apps دون الحاجة إلى لغات أو أدوات خاصة مثل Swift أو Java.
الفكرة بسيطة: تستخدم متصفح المستخدم نفسه لتقديم تجرِبة سريعة، قابلة للتثبيت، وتعمل حتى دون اتصال بالإنترنت.
تجمع PWA بين ميزات الويب والتطبيقات الأصلية:
- يمكن للمستخدم تثبيتها على الشاشة الرئيسية.
- تعمل في وضع offline بفضل التخزين المؤقت Cache.
- تُفتح بواجهة مستقلة دون شريط العنوان أو المتصفح.
- تُرسل إشعارات (Notifications) وتتعامل مع الخلفية Background Sync.
من الناحية التقنية، أي PWA يتكوّن من ثلاث ركائز أساسية:
- Manifest.json: يصف معلومات التطبيق (الاسم، الأيقونة، الألوان، صفحة البداية...).
- Service Worker: سكربت يعمل في الخلفية لتخزين الملفات وتوفير تجربة offline وتحديثات تلقائية.
- HTTPS: شرط إلزامي لضمان الأمان أثناء عمل الـ Service Worker.
النتيجة النهائية: تطبيق يمكنك تثبيته مباشرة من المتصفح على الهاتف أو الحاسوب دون المرور بالمتجر.
كيف نحول موقع عادي إلى PWA
التحويل يتم بإضافة ثلاث مكونات رئيسية داخل مشروعك الحالي:
- ملف
manifest.json
- سكربت
service worker
- تسجيل الـ service worker في صفحة الموقع
1. إنشاء ملف manifest.json
هو ملف بتنسيق JSON يحتوي على معلومات التطبيق مثل الاسم، الأيقونة، والألوان.
أنشئ الملف في جذر المشروع وضع فيه:
$$
{
"name": "My PWA App",
"short_name": "MyApp",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0A84FF",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
$$
ثم أضف الرابط داخل وسم <head> في index.html
$$
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#0A84FF">
$$
شرح بسيط لقيم ملف manifest.json:
- name: الاسم الكامل للتطبيق
يظهر في واجهة التثبيت وعلى شاشة التحميل الأولى.
مثال: "My PWA App"
- short_name: الاسم المختصر
يُعرض أسفل أيقونة التطبيق عند التثبيت على الهاتف أو سطح المكتب.
مثال: "MyApp"
- start_url: مسار البداية
الصفحة التي تُفتح عند تشغيل التطبيق. يمكن أن تكون / أو /home.
مثال: "/"
- scope: نطاق التصفح
يحدد الصفحات المسموح بها داخل التطبيق. أي روابط خارج هذا النطاق تُفتح في المتصفح العادي.
مثال: "/"
- display: نمط العرض
يحدد شكل التطبيق عند فتحه.
القيم الممكنة:
standalone: عرض كتطبيق بدون شريط المتصفح
fullscreen: ملء الشاشة
browser: عرض داخل المتصفح
مثال: "standalone"
- background_color: لون الخلفية أثناء التحميل
يظهر عند فتح التطبيق قبل تحميل المحتوى.
مثال: "#ffffff"
- theme_color: لون شريط الحالة (Status Bar)
يحدد اللون العلوي في واجهة النظام عند تشغيل التطبيق.
مثال: "#0A84FF"
- icons: أيقونات التطبيق
تُستخدم أثناء التثبيت وعرض الأيقونة على الجهاز.
عادةً تشمل صورتين 192×192 و512×512 بصيغة PNG.
2. إنشاء ملف Service Worker
ملف Service Worker هو سكربت يعمل في الخلفية خارج واجهة المتصفح، وظيفته الأساسية جعل التطبيق يعمل دون اتصال بالإنترنت وتخزين الملفات مؤقتًا.
لتمكينه، أنشئ ملف باسم sw.js في الملف الأساسي للمشروع.
مثال أساسي:
$$
const CACHE_NAME = 'pwa-cache-v1';
const FILES_TO_CACHE = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/icons/icon-192.png',
'/icons/icon-512.png',
'/offline.html'
];
// أثناء التثبيت يتم تخزين الملفات في الكاش
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(FILES_TO_CACHE))
);
});
$$
ملاحظات:
يجب أن يكون الموقع يعمل عبر HTTPS أو على localhost أثناء التطوير.
هذا السكربت يضيف وضع offline بسيط، وسنطوره في القسم القادم بإستراتيجية التخزين المؤقت (Cache Strategy).
إدارة التخزين المؤقت Cache Strategy
التخزين المؤقت هو ما يجعل تطبيق PWA سريعًا وقادرًا على العمل دون إنترنت.
هناك أكثر من إستراتيجية، لكن سنستخدم أبسطها ثم نطوّرها.
1. Cache First (الأولوية للكاش)
يفيد للملفات الثابتة مثل الصور والأيقونات.
$$
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
$$
2. Network First (الأولوية للشبكة)
يفيد للصفحات التي تتغير محتوياتها باستمرار مثل الأخبار أو API.
$$
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
.then(response => {
const clone = response.clone();
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
return response;
})
.catch(() => caches.match(event.request))
);
});
$$
3. Stale-While-Revalidate (عرض من الكاش ثم تحديثه)
يوفّر أفضل توازن بين السرعة والتحديث.
$$
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
const fetchPromise = fetch(event.request).then(networkResponse => {
caches.open(CACHE_NAME).then(cache =>
cache.put(event.request, networkResponse.clone())
);
return networkResponse;
});
return cached || fetchPromise;
})
);
});
$$
3. تسجيل الـ Service Worker في الموقع
بعد إنشاء الملف sw.js، نحتاج إلى تسجيله داخل الموقع حتى يتعرف عليه المتصفح ويبدأ بتنفيذه في الخلفية.
يتم التسجيل عادة في ملف JavaScript رئيسي مثل app.js أو داخل index.html.
مثال عملي:
$$
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/sw.js')
.then(registration => {
console.log('Service Worker مسجل بنجاح:', registration.scope);
})
.catch(error => {
console.error('فشل تسجيل Service Worker:', error);
});
});
}
$$
الشرح:
الشرط 'serviceWorker' in navigator يتأكد أن المتصفح يدعم هذه التقنية.
عند تحميل الصفحة (window.load)، يتم تسجيل ملف sw.js.
بعد التسجيل، المتصفح يُفعّل الـ Service Worker ويبدأ في مراقبة الطلبات.
ملاحظات مهمة:
يجب أن يكون المسار /sw.js ضمن نطاق الموقع (scope).
بعد أول تسجيل، التحديثات لا تُفعّل فورًا بل في الجولة التالية (بعد إغلاق التبويب وإعادة فتحه).
يمكن فحص الحالة من خلال DevTools → Application → Service Workers.
دعم العمل بدون اتصال Offline Support
لجعل الموقع يعمل في حالة انقطاع الإنترنت، نحتاج إلى صفحة بديلة تُعرض للمستخدم عندما لا يتوفر اتصال.
أنشئ صفحة بسيطة باسم offline.html:
$$
<!DOCTYPE html>
<html lang="ar">
<head>
<meta charset="UTF-8">
<title>لا يوجد اتصال</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 2rem; }
</style>
</head>
<body>
<h1>⚠️ لا يوجد اتصال بالإنترنت</h1>
<p>يرجى إعادة المحاولة لاحقًا.</p>
</body>
</html>
$$
ثم عدّل كود fetch داخل sw.js ليستخدمها عند انقطاع الاتصال:
$$
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
.catch(() => caches.match(event.request).then(resp => resp || caches.match('/offline.html')))
);
});
$$
الآن، عند فقد الاتصال، سيعرض التطبيق صفحة offline.html بدل رسالة خطأ المتصفح.
اختبار التطبيق
بعد الانتهاء من إعداد الملفات (manifest.json, sw.js, وoffline.html)، يجب التأكد أن التطبيق يعمل فعلاً كـ PWA قبل الانتقال لمرحلة النشر.
1. اختبار يدوي في المتصفح
- افتح الموقع عبر HTTPS أو من خلال localhost.
- افتح أدوات المطور (DevTools) → التبويب Application.
- تحقق من:
- وجود Manifest صالح.
- تسجيل Service Worker بنجاح.
- ظهور خيار “Add to Home Screen” على الهاتف أو المتصفح.
2. اختبار العمل دون اتصال
- افتح الموقع في المتصفح.
- افصل الإنترنت أو فعّل وضع Offline من DevTools → Network → Offline.
- أعد تحميل الصفحة.
- إذا ظهرت صفحة
offline.html أو المحتوى المخزّن، فهذا يعني أن الـ Service Worker يعمل بشكل صحيح.
3. اختبار باستخدام Lighthouse
- افتح DevTools → Lighthouse.
- اختر الفئة Progressive Web App.
- اضغط Analyze page load.
- انتظر التقرير. إذا كانت جميع النقاط خضراء أو قريبة من 100%، فهذا يعني أن التطبيق جاهز للنشر.
4. اختبار على الهاتف
- افتح الموقع من Chrome أو Edge أو Safari على الهاتف.
- يجب أن يظهر إشعار تثبيت التطبيق (Install App).
- ثبّت التطبيق ولاحظ أنه يعمل في واجهة مستقلة (دون شريط المتصفح).
نشر التطبيق على المتاجر Google Play, Apple Store, Microsoft Store
بعد التأكد من أن تطبيقك يعمل كـ PWA، يمكنك تحويله إلى تطبيق قابل للنشر بسهولة باستخدام PWABuilder.
1. استخدام PWABuilder
- ادخل إلى https://www.pwabuilder.com
- ضع رابط موقعك (HTTPS).
- سيقوم الموقع بتحليل تطبيقك ويعطيك تقريراً حول جاهزيته للنشر.
- يمكنك تحميل الحزم الجاهزة لكل منصة:
- Android (APK/AAB) — للنشر على Google Play.
- iOS (Package) — للنشر على Apple Store.
- Windows (MSIX) — للنشر على Microsoft Store.
2. التحقق قبل النشر
- اختبر الحزمة على الأجهزة الحقيقية.
- تأكد من أن الأيقونات، الاسم، والألوان تظهر بشكل صحيح.
- فعّل HTTPS، وحدّث manifest و service worker إذا تطلب الأمر.
3. النشر
- سجّل حساب مطوّر في كل متجر (Google, Apple, Microsoft).
- ارفع الحزمة المناسبة واملأ معلومات التطبيق (الاسم، الوصف، الصور).
- بعد المراجعة، سيكون تطبيقك متاحاً للمستخدمين مثل أي تطبيق أصلي.
ملاحظة مهمة قبل النشر
عند إنشاء الحزم عبر PWABuilder، ستحصل على مفاتيح توقيع signing keys خاصة بالتطبيق.
يجب أن تحتفظ بهذه المفاتيح في مكان آمن، فهي تستخدم لتحديث التطبيق مستقبلاً على المتاجر.
- لا تشاركها مع أي شخص.
- لا ترفعها إلى GitHub أو أي مستودع عام.
- احتفظ بنسخة احتياطية مشفّرة منها (مثلاً في Google Drive أو جهاز مشفّر).
إذا فقدت المفاتيح، لن تتمكن من تحديث التطبيق بعد نشره.
الخاتمة
بعد أن أنشأنا ملف manifest.json، وأضفنا Service Worker لإدارة التخزين المؤقت، واختبرنا التطبيق على المتصفح، أصبح لدينا تطبيق ويب تقدّمي (PWA) جاهز للتثبيت والاستخدام.
باستخدام أدوات مثل PWABuilder يمكننا بسهولة إنشاء حزم تثبيت لأنظمة Android (APK) وiOS وWindows، وتجهيزها للنشر على المتاجر الرسمية.
تذكّر أن تحتفظ بمفاتيح التوقيع في مكان آمن لتتمكن من تحديث التطبيق لاحقًا.
بهذه الخطوات، يمكن تحويل أي موقع بسيط إلى تطبيق متكامل يعمل على جميع المنصات باستخدام JavaScript فقط، بدون أطر عمل إضافية أو تكاليف.
النتيجة: تطبيق سريع، قابل للتثبيت، يدعم العمل دون اتصال، ويمنح تجربة مستخدم مماثلة للتطبيقات الأصلية.