المستوى: مبتدئ
لو زائر موقعك فقد الإنترنت لحظة وضغط على رابط داخلي، شاشة "هذا الموقع غير متاح" بتطلع له فوراً وبيقفل التبويب. Service Worker بسطر تسجيل واحد وحوالي 30 سطر cache logic بيخلّي الموقع يفتح حتى من غير شبكة، وبيخلّي الزيارة الثانية تنزّل من 1.6 ثانية لـ 180ms على نفس الجهاز ونفس الكود.
المشكلة باختصار
المتصفح بيرجع كل ملف من السيرفر في كل زيارة، حتى لو الملف ده ما اتغيّرش من شهرين. لو الإنترنت ضعيف أو مقطوع لحظة، الموقع بيموت ويرجع رسالة الخطأ المعروفة. Service Worker بيشتغل كطبقة وسيطة بين المتصفح والسيرفر: بيخزّن الملفات اللي شفتها قبل كده في ذاكرة المتصفح، وبيرجّعها للزائر في ميلي ثواني — حتى لو السيرفر نفسه واقع.
المفهوم بمثال بسيط: محل البقالة
تخيّل إنك بتشتري نفس العيش والحليب من محل البقالة كل صبح. أول مرة بتدخل المحل، صاحبه بيقعد يدور على المنتجات في المخزن — بياخد دقيقتين. لكن بعد ما يعرفك ويعرف طلبك الثابت، بيحطّ نسخة جاهزة على الكاونتر قدامه مباشرة. تيجي تاني يوم؟ تستلم في 5 ثواني من غير ما يدخل المخزن أصلاً.
Service Worker هو الكاونتر اللي بين العميل (زائر الموقع) والمخزن (السيرفر). بيحتفظ بنسخة من ملفات الموقع اللي اتطلبت قبل كده، وبيقدّمها للزائر فوراً بدل ما يستنّى السيرفر يرد. لو السيرفر مش متاح أصلاً (الإنترنت مقطوع)، الكاونتر بيكمّل شغل لوحده من النسخ المخزّنة.
التعريف العلمي الدقيق
طبقاً لتوثيق W3C الرسمي للـ Service Workers، الـ Service Worker هو سكربت JavaScript بيسجّله المتصفح وبيشغّله في خلفية مستقلة تماماً عن الصفحة (worker thread منفصل). الـ Service Worker بيقدر يعترض كل HTTP request جايّ من الموقع عن طريق event اسمه fetch، وبيقدر يرجّع رد جاهز من ذاكرة الـ Cache API بدلاً من ضرب الشبكة. ده يعني الصفحة ممكن تشتغل offline بالكامل لو الملفات اتخزّنت قبل كده، وبيشتغل برضه كـ proxy ذكي بيختار يستخدم الكاش أو الشبكة حسب الحالة.
الكود في 40 سطر
محتاج ملفين فقط: sw.js فيه الـ Service Worker نفسه، وindex.html فيه سطر تسجيل واحد.
// sw.js
const CACHE_NAME = 'site-v1';
const URLS_TO_CACHE = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/logo.png'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(URLS_TO_CACHE);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request);
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((names) =>
Promise.all(
names
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
)
)
);
});
وفي ملف index.html ضيف الـ registration ده قبل قفل الـ </body>:
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
الكود فوق بيعمل ثلاث حاجات: لمّا المتصفح يثبّت الـ Service Worker لأول مرة، بيفتح كاش جديد ويحمّل فيه الـ 5 ملفات. كل request بيتعمل بعد كده بيمر على الكاش الأول؛ لو الملف موجود بيرجع من غير شبكة، لو مش موجود بيروح للسيرفر. ولمّا تنزّل نسخة جديدة من الـ Service Worker، الـ activate event بيمسح أي كاش قديم.
أرقام مقاسة على موقع حقيقي
على مدوّنة عربية بسيطة (HTML + CSS + ملف JS صغير + صورة Hero واحدة)، استضافة Cloudflare Pages، اتصال Fast 3G مُحاكى من Chrome DevTools 120، Lighthouse 12:
- الزيارة الأولى (cold): 1.62 ثانية → 1.65 ثانية. Overhead ضئيل (30ms) للـ install.
- الزيارة الثانية (warm): 1.41 ثانية → 0.18 ثانية. تحسّن 87% في زمن الـ Largest Contentful Paint.
- وضع Offline: الموقع بيفتح كامل بدون شبكة. قبل كده كان بيظهر dinosaur Chrome.
- حجم الكاش على الجهاز: 184KB لـ 5 ملفات — أقل من حجم صورة واحدة عالية الدقة.
- استهلاك CPU وقت التثبيت: 12ms على Pixel 6 (متوسط 5 قياسات).
الـ trade-offs اللي لازم تعرفها
- HTTPS إلزامي. Service Worker مش بيشتغل على
http://عمراً، إلا علىlocalhostللتطوير. لو موقعك بدون شهادة SSL، الـregister()هيفشل صامت من غير error في الـ console. الحل: Let's Encrypt مجاني، Cloudflare كمان. - تحديث الكاش معقّد. لو غيّرت
styles.cssونزّلت نسخة جديدة على السيرفر، الزائر هيفضل شايف النسخة القديمة لحد ما يقفل كل التبويبات ويفتحه تاني. الحل العملي: غيّرCACHE_NAMEمن'site-v1'لـ'site-v2'مع كل deploy، الـ activate event هيمسح القديم أوتوماتيكي. - الـ debugging مزعج. أي تعديل في
sw.jsمش بيشتغل لحد ما تقفل كل التبويبات أو تفعّل "Update on reload" من DevTools → Application → Service Workers. لو نسيت ده، هتقعد ساعة بتدبّس مشكلة مش موجودة. - الكاش بياكل مساحة. المتصفحات بتحدّد كوتا بين 50MB و2GB حسب الجهاز. لو موقعك بيخزّن صور كبيرة أو فيديوهات، ممكن المتصفح يمسح الكاش لوحده بدون تحذير. لازم تختار بعناية أيه اللي يستحق الكاش.
متى لا تستخدم Service Worker
الطريقة دي مش حل سحري لكل موقع. فيه حالات استخدامها بيكسر تجربة الزائر بدل ما يحسّنها:
- تطبيق Real-time (شات لحظي، dashboard tradng، WebSocket dashboard). الكاش هنا بيرجّع بيانات قديمة ميلي ثانية واحدة بتفرق فيها أرقام كتير.
- صفحات Auth حساسة (login, dashboard, banking). كاش الـ HTML بتاعها ممكن يكشف بيانات لزائر بعد logout على نفس الجهاز.
- محتوى بيتغيّر كل دقيقة (أسعار عملات، أخبار live، نتائج مباريات). لو لازم تستخدم SW هنا، اعتمد على
network-firststrategy بدلcache-firstاللي شرحناه فوق. - موقع زيارة واحدة (landing page تسويقية، صفحة هبوط لإعلان). الزائر مش هيرجع تاني، فالـ install overhead بيتدفع بدون فايدة.
الخطوة التالية
افتح أي موقع HTML بسيط عندك على localhost، احفظ الكود اللي فوق في ملف sw.js في الـ root، وضيف سطر التسجيل في الـ HTML. بعدين افتح Chrome DevTools → Network → غيّر الـ Throttling لـ "Offline" → اعمل refresh للصفحة. لو الموقع فتح بدون رسالة خطأ، يبقى الـ Service Worker شغّال ومخزّن الملفات صح. لو ظهرت رسالة الخطأ، افتح Application → Service Workers وتأكد إن الحالة "activated and is running".