تشترك معظم التطبيقات الشهيرة مثل مُساعد جوجل Google Assistant وتطبيق الدردشة سلاك Slack وتطبيق اللقاءات الافتراضية زووم Zoom بأنها تُقدّم خدمات محدودة جدًا في وضع انقطاع الاتصال، أي أنه يُمكن تشغيلها والدخول إليها ومعاينة بعض الواجهات فيها على الرغم من انقطاع الاتصال.
تُبين اللقطات التالية أن تطبيقات المنصات الخاصة تعرض شيئًا ما في وضع انقطاع الاتصال. مثلًا اللقطة التالية للمُساعد Google Assistant:
أما اللقطة التالية لتطبيق سلاك Slack:
واللقطة التالية لتطبيق زووم Zoom:
على النقيض من ذلك، لا نحصل على شيء على الويب عند انقطاع الاتصال. مثلًا، قد يُظهر المتصفح Chrome اللعبة البسيطة dino على أنظمة التشغيل iOS:
وعلى أنظمة التشغيل macOS:
سنتعلم في هذا المقال كيفية إنشاء صفحة احتياطية لوضع انقطاع الاتصال وإضافتها إلى تطبيقات الويب التقدمية PWA.
إنشاء صفحة احتياطية باستخدام عامل خدمة مخصص
من المناسب تخصيص سلوك صفحات الويب التقدميّة في حال انقطاع الاتصال لاسيما أنه أصبح بالإمكان توفير تجربة استخدام مخصصة باستخدام عامل خدمة service worker وواجهة برمجة التطبيقات ذات التخزين المؤقت Cache Storage API . يُمكن عادًة إنشاء صفحة احتياطية بسيطة تُبين للمستخدم أنه في وضع انقطاع الاتصال، إلا أنه يُمكن أيضًا إيجاد حلول إبداعية أجمل. لنلقي نظرة مثلًا على موقع الحجز trivago والذي يوفر في صفحته الاحتياطية لعبة متاهة مسلية للمستخدم خلال انقطاع الاتصال مع زر لتجريب إعادة الاتصال Reconnect وعدّاد تنازلي لإظهار الزمن المتبقي لإعادة محاولة الاتصال تلقائيًا كما يُبين الشكل التالي:
تسجيل عامل الخدمة
لإنشاء الصفحة الاحتياطية، يجب أولًا تسجيل عامل خدمة في الصفحة الأساسية كما تُبين الشيفرة التالية والتي تُنفّذ عند تحميل التطبيق:
window.addEventListener("load", () => { if ("serviceWorker" in navigator) { navigator.serviceWorker.register("service-worker.js"); } });
شيفرة عامل الخدمة
نعرض فيما يلي مثالًا عن شيفرة عامل الخدمة والتي قد تبدو للوهلة الأولى معقدة قليلًا ولذا أُضيفت بعض التعليقات المُساعدّة. تكمن الفكرة بالتخزين المؤقت المسبق لملف ندعوه offline.html
والذي يعرضه المتصفح في حال فشل طلبات التنقل navigation فقط، وفي بقية الحالات يتابع المتصفح معالجاته الاعتيادية.
/* معلومات رخصة الاستخدام Copyright 2015, 2019, 2020, 2021 Google LLC. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // (1) // يُمكن إضافة أي تعليق مناسب لمنقح الصياغة // eslint-disable-next-line no-unused-vars const OFFLINE_VERSION = 1; const CACHE_NAME = "offline"; يُمكن تخصيص مُحدّد موارد URL مختلف إذا دعت الحاجة // const OFFLINE_URL = "offline.html"; self.addEventListener("install", (event) => { event.waitUntil( (async () => { const cache = await caches.open(CACHE_NAME); // (2) await cache.add(new Request(OFFLINE_URL, { cache: "reload" })); })() ); إرغام عامل الخدمة المنتظر ليصبح العامل النشط // self.skipWaiting(); }); self.addEventListener("activate", (event) => { event.waitUntil( (async () => { // السماح بالتنقل قبل التحميل إذا كان ذلك مدعومًا // See https://developers.google.com/web/updates/2017/02/navigation-preload if ("navigationPreload" in self.registration) { await self.registration.navigationPreload.enable(); } })() ); // الطلب من عامل الخدمة أن يتحكم بالصفحة فورًا self.clients.claim(); }); self.addEventListener("fetch", (event) => { إذا كان الطلب لصفحة HTML نستدعي event.respondWith// if (event.request.mode === "navigate") { event.respondWith( (async () => { try { نحاول أولًا استخدام جواب التنقل قبل التحميل إذا كان مدعومًا // const preloadResponse = await event.preloadResponse; if (preloadResponse) { return preloadResponse; } تجريب الشبكة أولًا // const networkResponse = await fetch(event.request); return networkResponse; } catch (error) { // (3) console.log("Fetch failed; returning offline page instead.", error); const cache = await caches.open(CACHE_NAME); const cachedResponse = await cache.match(OFFLINE_URL); return cachedResponse; } })() ); } // (4) });
-
(1) تؤدي زيادة قيمة المتغير
OFFLINE_VERSION
إلى تنشيط حدث التثبيت وتحديث الموارد المخبئة سابقًا عبر الشبكة، المتغير مُعرّف عمدًا مع أنه غير مستخدم. - (2) يؤدي ضبط {cache: 'reload'} في الطلب الجديد إلى استيفاء الطلب HTTP من الشبكة وليس من الذاكرة المؤقتة.
- (3) رُفع استثناء يُنشّط الالتقاط catch والذي يكون على الأغلب بسبب خطأ في الشبكة، ولن تُستدعى catch إذا أعادت fetch جواب HTTP صالح، مع كود الجواب محصور بين
- 4xx - 5xx. (4) إذا كان شرط التعليمة if خاطئًا فلن يقاطع معالج الجلب fetch الطلب، يُمكن أن يُستدعى التابع event.respondWith في حال وجود معالجات جلب أخرى مسجلة، إذا لم يستدع أي معالج جلب التابع event.respondWith فعندها يعالج المتصفح الطلب كما لو أنه لا يوجد عامل خدمة.
الصفحة الاحتياطية لوضع انقطاع الاتصال
يُعدّ ملف الصفحة الاحتياطية offline.html
المكان المناسب لإظهار بعض الإبداع لجذب المستخدم وإبراز العلامة التجارية للمنتج مثلًا، حيث يُمكن تكييف هذا الملف مع الاحتياجات المطلوبة. يُبين المثال التالي الحد الأدنى الممكن القيام به كإظهار زر إعادة التحميل، إضافًة لمحاولات إعادة التحميل الآلية والتي تعتمد على حدث الاتصال online
وعلى اقتراع خادم منتظم regular server polling.
يجب تخزين جميع الموارد التي تتطلبها الصفحة الاحتياطية لوضع انقطاع الاتصال. من الحلول المُمكنة لذلك تضمين كل شيء وبذا تُصبح الصفحة الاحتياطية حاوية لنفسها، وهو ما يُبينه المثال التالي:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>You are offline</title> <!-- Inline the page's stylesheet. --> <style> body { font-family: helvetica, arial, sans-serif; margin: 2em; } h1 { font-style: italic; color: #373fff; } p { margin-block: 1rem; } button { display: block; } </style> </head> <body> <h1>You are offline</h1> <p>Click the button below to try reloading.</p> <button type="button">⤾ Reload</button> <!-- Inline the page's JavaScript file. --> <script> // ميزة إعادة التحميل اليدوي document.querySelector("button").addEventListener("click", () => { window.location.reload(); }); //(1) window.addEventListener('online', () => { window.location.reload(); }); //(2) async function checkNetworkAndReload() { try { const response = await fetch('.'); // التحقق من الحصول على جواب صالح من الخادم if (response.status >= 200 && response.status < 500) { window.location.reload(); return; } } catch { // تجاهل إذ لا يُمكن الاتصال مع الخادم } window.setTimeout(checkNetworkAndReload, 2500); } checkNetworkAndReload(); </script> </body> </html>
حيث أن:
- (1) متابعة تغييرات حالة الشبكة وإعادة التحميل عند انقطاع الاتصال حيث ينشط المعالج التالي في حالة انقطاع الاتصال كليًا.
- (2) إعادة تحميل الصفحة إذا كان الخادم متجاوبًا حيث ينشط المعالج التالي في حال انقطاع الاتصال عن الجهاز أو عدم تجاوب الخادم بشكل صحيح.
مثال توضيحي
يُمكن معاينة الصفحة الاحتياطية في المثال والموضح لقطة منه أدناه. كما يُمكن الحصول على الشيفرة المصدرية الموافقة من المستودع Glitch.
يُمكن، بعد الانتهاء من إنشاء الصفحة الاحتياطية لوضع عدم الاتصال، تحقيق إمكانية تثبيت التطبيق بإضافة بيان تطبيق الويب واختيار استراتيجية التثبيت. كما يُمكن، للسهولة، استخدام مكتبات Workbox.js والتي توفر الشيفرة اللازمة لإنشاء الصفحة الاحتياطية.
ترجمة -وبتصرف- للمقال Create an offline fallback page للمؤلفين: Thomas Steiner و Pete LePage.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.