كيفية جعل تطبيقات الويب الخاصة بك تعمل دون اتصال

قوة جافا سكريبت ومتصفح API

أصبح العالم أكثر ترابطًا - فقد زاد عدد الأشخاص الذين لديهم إمكانية الوصول إلى الإنترنت إلى 4.5 مليار .

صورة

لكن هذه البيانات لا تعكس عدد الأشخاص الذين لديهم اتصال إنترنت بطيء أو مقطوع. حتى في الولايات المتحدة ، لا يمكن لـ 4.9 مليون منزل الوصول إلى الإنترنت السلكي بسرعة تفوق 3 ميغابت في الثانية.

لا يزال بقية العالم - أولئك الذين لديهم إمكانية الوصول الموثوق إلى الإنترنت - عرضة لفقد الاتصال. تتضمن بعض العوامل التي قد تؤثر على جودة اتصال الشبكة:

  • تغطية سيئة من مزود.
  • الظروف الجوية القاسية.
  • انقطاع التيار الكهربائي.
  • المستخدمون الذين يقعون في مناطق ميتة ، مثل المباني التي تمنع اتصالات الشبكة الخاصة بهم.
  • قطار السفر ونفق السفر.
  • الاتصالات التي يتم التحكم فيها بواسطة جهة خارجية وتكون محدودة الوقت.
  • الممارسات الثقافية التي تتطلب الوصول المحدود أو بدون الوصول إلى الإنترنت في أوقات أو أيام محددة.

بالنظر إلى هذا ، من الواضح أننا يجب أن نفكر في تجربة مستقلة عند تطوير وإنشاء التطبيقات.

إديسون البرمجيات - تطوير الشبكة
تمت ترجمة هذه المقالة بدعم من EDISON Software ، وهي شركة تنفذ طلبيات ممتازة من جنوب الصين ، وتطور أيضًا تطبيقات الويب والمواقع .
لقد أتيحت لي الفرصة مؤخرًا لإضافة استقلال إلى تطبيق موجود باستخدام عمال الخدمة وتخزين ذاكرة التخزين المؤقت و IndexedDB. تم تخفيض العمل الفني اللازم للتطبيق في وضع عدم الاتصال إلى أربع مهام منفصلة ، والتي سأناقشها في هذا المنشور.

عمال الخدمة


يجب ألا تعتمد التطبيقات التي تم إنشاؤها للاستخدام في وضع عدم الاتصال بالشبكة. من الناحية النظرية ، يكون هذا ممكنًا فقط في حالة وجود خيارات نسخ احتياطي في حالة حدوث فشل.

إذا فشل تحميل تطبيق الويب ، يجب أن نأخذ موارد المتصفح في مكان ما (HTML / CSS / JavaScript). من أين تأتي هذه الموارد ، إن لم يكن من طلب شبكة؟ ماذا عن ذاكرة التخزين المؤقت. يوافق معظم الأشخاص على أنه من الأفضل توفير واجهة مستخدم قد تكون قديمة من صفحة فارغة.

المتصفح باستمرار يستعلم البيانات. لا تزال تتطلب خدمة التخزين المؤقت للبيانات كاحتياطي منا أن نعترض بشكل أو بآخر طلبات المتصفح وكتابة قواعد التخزين المؤقت. هذا هو المكان الذي يلعب فيه عمال الخدمة - فكر فيهم كوسيط.

صورة

عامل الخدمة هو مجرد ملف JavaScript يمكن من خلاله الاشتراك في الأحداث وكتابة قواعدنا الخاصة للتخزين المؤقت ومعالجة فشل الشبكة.
لنبدأ.

يرجى ملاحظة: تطبيق تجريبي لدينا

خلال هذا المنشور ، سنضيف وظائف قائمة بذاتها إلى التطبيق التجريبي. التطبيق التجريبي عبارة عن صفحة بسيطة لأخذ / تأجير الكتب في المكتبة. سيتم تقديم التقدم كسلسلة من صور GIF ، واستخدام محاكاة Chrome DevTools غير المتصلة بالإنترنت.

هذه هي الحالة الأولية:

صورة

المهمة 1 - التخزين المؤقت للموارد الثابتة


الموارد الثابتة هي الموارد التي لا تتغير في كثير من الأحيان. قد تندرج HTML و CSS و JavaScript والصور في هذه الفئة. يحاول المستعرض تحميل موارد ثابتة باستخدام طلبات يمكن اعتراضها بواسطة عامل الخدمة.

لنبدأ بتسجيل عامل الخدمة لدينا.

if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/sw.js'); }); } 

عمال الخدمة هم عمال الويب تحت غطاء محرك السيارة ، وبالتالي يجب استيرادها من ملف جافا سكريبت منفصل. يتم التسجيل باستخدام طريقة register بعد تحميل الموقع.
الآن وبعد أن تم تحميل عامل خدمة ، فلنقم بتخزين مواردنا الثابتة.

 var CACHE_NAME = 'my-offline-cache'; var urlsToCache = [ '/', '/static/css/main.c9699bb9.css', '/static/js/main.99348925.js' ]; self.addEventListener('install', function(event) { event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { return cache.addAll(urlsToCache); }) ); }); 

نظرًا لأننا نتحكم في عناوين URL للموارد الثابتة ، يمكننا تخزينها على الفور بعد تهيئة عامل الخدمة باستخدام Cache Storage .

صورة

الآن وقد امتلأت ذاكرة التخزين المؤقت الخاصة بنا بالموارد الثابتة المطلوبة حديثًا ، فلنقم بتحميل هذه الموارد من ذاكرة التخزين المؤقت في حالة فشل الطلب.

 self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request).catch(function() { caches.match(event.request).then(function(response) { return response; } ); ); }); 

يتم تشغيل حدث fetch كل مرة يقوم فيها المستعرض بطلب. يتمتع معالج حدث fetch الجديد الخاص بنا الآن بمنطق إضافي لإرجاع الاستجابات المخزنة مؤقتًا في حالة انقطاع الشبكة.

عرض رقم 1


صورة

يمكن الآن تطبيقنا التجريبي خدمة الموارد الثابتة حاليا! لكن أين هي بياناتنا؟

المهمة 2 - التخزين المؤقت للموارد الحيوية


عادةً ما تطلب التطبيقات ذات الصفحة الواحدة (SPA) البيانات تدريجيًا بعد التحميل الأولي للصفحة ، وتطبيقنا التجريبي ليس استثناءً - لم يتم تحميل قائمة الكتب على الفور. تأتي هذه البيانات عادةً من طلبات XHR التي ترجع الردود التي تتغير بشكل متكرر لتوفير حالة جديدة للتطبيق - وبالتالي فهي ديناميكية.

تشبه تخزين الموارد الديناميكية في الواقع إلى حد كبير الموارد المخزنة مؤقتًا - والفرق الرئيسي هو أننا نحتاج إلى تحديث ذاكرة التخزين المؤقت كثيرًا. من الصعب للغاية إنشاء قائمة كاملة بجميع طلبات XHR الديناميكية المحتملة ، لذلك سنقوم بتخزينها مؤقتًا عند وصولها ، بدلاً من وجود قائمة محددة مسبقًا ، كما فعلنا مع الموارد الثابتة.

ألق نظرة على معالج fetch لدينا:

 self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request).catch(function() { caches.match(event.request).then(function(response) { return response; } ); ); }); 

يمكننا تخصيص هذا التطبيق عن طريق إضافة بعض الرموز التي تخبئ الطلبات والطلبات الناجحة. هذا يضمن أن نضيف باستمرار طلبات جديدة إلى ذاكرة التخزين المؤقت لدينا وتحديث البيانات المخزنة مؤقتا باستمرار.

 self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request) .then(function(response) { caches.open(CACHE_NAME).then(function(cache) { cache.put(event.request, response); }); }) .catch(function() { caches.match(event.request).then(function(response) { return response; } ); ); }); 

يحتوي Cache Storage لدينا حاليًا على العديد من الإدخالات.

صورة

عرض رقم 2


صورة

يبدو العرض التوضيحي لدينا الآن هو نفسه في التمهيد ، بغض النظر عن حالة شبكتنا!

ممتاز. دعنا الآن نحاول استخدام تطبيقنا.

صورة

لسوء الحظ ، رسائل الخطأ في كل مكان. يبدو أن جميع تفاعلاتنا مع الواجهة لا تعمل. لا أستطيع اختيار أو تسليم الكتاب! ما يحتاج إلى إصلاح؟

المهمة 3 - بناء واجهة مستخدم متفائلة


في الوقت الحالي ، المشكلة في تطبيقنا هي أن منطق جمع البيانات الخاص بنا لا يزال يعتمد بشكل كبير على استجابات الشبكة. يرسل إجراء تسجيل الوصول أو المغادرة طلبًا إلى الخادم ويتوقع استجابة ناجحة. هذا شيء رائع بالنسبة إلى تناسق البيانات ، ولكنه سيء ​​بالنسبة لتجربتنا المستقلة.

لكي تعمل هذه التفاعلات في وضع عدم الاتصال ، نحتاج إلى جعل تطبيقنا أكثر تفاؤلاً . لا تتطلب التفاعلات المتفائلة استجابة من الخادم وتعرض عرضًا محدثًا للبيانات عن طيب خاطر. يتم delete العملية المتفائلة المعتادة في معظم تطبيقات الويب - لماذا لا نعطي المستخدم ملاحظات فورية إذا كان لدينا بالفعل جميع المعلومات اللازمة؟

من السهل نسبيا تنفيذ فصل تطبيقنا عن الشبكة باستخدام نهج متفائل.

 case CHECK_OUT_SUCCESS: case CHECK_OUT_FAILURE: list = [...state.list]; list.push(action.payload); return { ...state, list, }; case CHECK_IN_SUCCESS: case CHECK_IN_FAILURE; list = [...state.list]; for (let i = 0; i < list.length; i++) { if (list[i].id === action.payload.id) { list.splice(i, 1, action.payload); } } return { ...state, list, }; 

المفتاح هو التعامل مع إجراءات المستخدم بنفس الطريقة - بغض النظر عما إذا كان طلب الشبكة ناجحًا أم لا. مقتطف الشفرة أعلاه مأخوذ من مخفض التكرار في تطبيقنا ، FAILURE إطلاق SUCCESS FAILURE وفقًا لتوفر الشبكة. بغض النظر عن كيفية إكمال طلب الشبكة ، سنقوم بتحديث قائمة الكتب لدينا.

عرض رقم 3


صورة

يحدث تفاعل المستخدم الآن عبر الإنترنت (وليس حرفيًا). يقوم أزرار "تسجيل الوصول" و "تسجيل الخروج" بتحديث الواجهة وفقًا لذلك ، على الرغم من أن الرسائل الحمراء لوحدة التحكم تظهر أن طلبات الشبكة لا يتم تنفيذها.

حسن! هناك مشكلة صغيرة واحدة فقط في التقديم دون الاتصال بالإنترنت ...

لا نفقد التغيير لدينا؟

صورة

مهمة 4 - إجراءات المستخدم قائمة انتظار للتزامن


نحتاج إلى تتبع الإجراءات التي يقوم بها المستخدم عندما كان غير متصل بالإنترنت ، حتى نتمكن من مزامنتها مع خادمنا عندما يعود المستخدم إلى الشبكة. هناك العديد من آليات التخزين في المستعرض التي يمكن أن تكون بمثابة قائمة انتظار من الإجراءات ، وسوف نستخدم IndexedDB. يوفر IndexedDB بعض الأشياء التي لن تحصل عليها من LocalStorage:

  • عمليات حظر غير متزامنة
  • حدود تخزين أعلى بكثير
  • إدارة المعاملات

انظر إلى رمز المخفض القديم لدينا:

 case CHECK_OUT_SUCCESS: case CHECK_OUT_FAILURE: list = [...state.list]; list.push(action.payload); return { ...state, list, }; case CHECK_IN_SUCCESS: case CHECK_IN_FAILURE; list = [...state.list]; for (let i = 0; i < list.length; i++) { if (list[i].id === action.payload.id) { list.splice(i, 1, action.payload); } } return { ...state, list, }; 

دعونا تعديله لتخزين أحداث تسجيل الوصول والمغادرة في IndexedDB أثناء الحدث FAILURE .

 case CHECK_OUT_FAILURE: list = [...state.list]; list.push(action.payload); addToDB(action); // QUEUE IT UP return { ...state, list, }; case CHECK_IN_FAILURE; list = [...state.list]; for (let i = 0; i < list.length; i++) { if (list[i].id === action.payload.id) { list.splice(i, 1, action.payload); addToDB(action); // QUEUE IT UP } } return { ...state, list, }; 

هنا هو تطبيق إنشاء IndexedDB جنبا إلى جنب مع addToDB addToDB.

 let db = indexedDB.open('actions', 1); db.onupgradeneeded = function(event) { let db = event.target.result; db.createObjectStore('requests', { autoIncrement: true }); }; const addToDB = action => { var db = indexedDB.open('actions', 1); db.onsuccess = function(event) { var db = event.target.result; var objStore = db .transaction(['requests'], 'readwrite') .objectStore('requests'); objStore.add(action); }; }; 

الآن وقد تم تخزين جميع إجراءات المستخدم غير المتصل بالإنترنت في ذاكرة المتصفح ، يمكننا استخدام مستمع أحداث المستعرض online لمزامنة البيانات عند استعادة الاتصال.

 window.addEventListener('online', () => { const db = indexedDB.open('actions', 1); db.onsuccess = function(event) { let db = event.target.result; let objStore = db .transaction(['requests'], 'readwrite') .objectStore('requests'); objStore.getAll().onsuccess = function(event) { let requests = event.target.result; for (let request of requests) { send(request); // sync with the server } }; }; }); 

في هذه المرحلة ، يمكننا مسح قائمة انتظار جميع الطلبات التي أرسلناها بنجاح إلى الخادم.

عرض رقم 4


التجريبي النهائي يبدو أكثر تعقيدا قليلا. على اليمين ، في الإطار الطرفي المظلم ، يتم تسجيل جميع أنشطة واجهة برمجة التطبيقات. يتضمن العرض التوضيحي عدم الاتصال بالإنترنت واختيار كتب متعددة والعودة عبر الإنترنت.

صورة

من الواضح أن الطلبات المقدمة في وضع عدم الاتصال تم وضعها في قائمة الانتظار وإرسالها مرة واحدة عندما يعود المستخدم عبر الإنترنت.

أسلوب "التشغيل" هذا ساذج بعض الشيء - على سبيل المثال ، ربما لا نحتاج إلى تقديم طلبين إذا أخذنا ونرجع نفس الكتاب. لن تعمل أيضًا إذا كان العديد من الأشخاص يستخدمون نفس التطبيق.

هذا كل شيء


الخروج وجعل تطبيقات الويب الخاصة بك حاليا! يوضح هذا المنشور بعض الأشياء الكثيرة التي يمكنك القيام بها لإضافة ميزات مستقلة إلى تطبيقاتك ، وهي بالتأكيد ليست شاملة.
لمعرفة المزيد ، تحقق من أساسيات الويب من Google . لمشاهدة تطبيق آخر في وضع عدم الاتصال ، تحقق من هذا الكلام .


اقرأ أيضا بلوق
شركة إديسون:


20 مكتبة لل
مذهلة تطبيق دائرة الرقابة الداخلية

Source: https://habr.com/ru/post/ar474374/


All Articles