Офлайн-вебзастосунки, або Чому варто використовувати Service Workers
Чи чули ви колись про офлайн-вебзастосунки чи офлайн-вебсайти?
Я впевнений, що так, а якщо не чули, зараз ще й дізнаєтесь, як це працює, чому працює і кому це потрібно.
У назві цієї статті присутня назва технології, про яку йтиметься далі, тобто Service Workers, але перед тим, як ми зануримося в подробиці реалізації та використання, з’ясуймо, яку проблему вирішує саме ця технологія.
Проблема
Уявімо собі ситуацію... насправді на момент написання цієї статті я якраз перебуваю в такій ситуації, тому це перше, що прийшло мені на думку. Отже ви працюєте, наприклад, пишете статтю про Service Workers, а для написання використовуєте сервіс Google Docs, адже це так зручно.
Під час написання тексту в Google Docs ви розумієте, що у вас зник інтернет-звʼязок. Причини можуть бути різні, але ситуація така. Ви вже починаєте хвилюватись, бо потрібно швидко зберегти текст кудись в інше місце, щоб не втратити його, але випадково перезавантажуєте сторінку й думаєте «Ну все, вся робота втрачена». Але на ваше здивування сторінка перезавантажується, а весь текст, який ви писали, залишається на своєму місці, і ви навіть можете продовжити свою роботу далі, адже все працює. Не зрозуміло як, але працює.
Як це працює, або офлайн-робота
Офлайн-робота вебзастосунку зазвичай реалізується через такий інструмент як Service Worker.
Service Worker — це спеціальний скрипт, що працює в фоновому режимі та не залежить від сторінки, на якій перебуває користувач. Оскільки це фоновий скрипт, то він може працювати та виконувати операції й тоді, коли користувач не переглядає вебсайт.
Загалом офлайн-робота сайту в будь-якому випадку буде базуватись на кешуванні ресурсів та контенту сайту: це одна з задач, які виконує Service Worker. Є й інші, а саме: пуш-нотифікації та оновлення даних в фоновому режимі (незалежно від сторінки чи контексту).
Кешування — це процес зберігання копій даних у тимчасовому сховищі, відомому як кеш. Цей процес дозволяє швидко отримати доступ до цих даних під час наступних запитів. У контексті веброзробки кешування зазвичай використовується для зберігання вебресурсів, як-от HTML-сторінки, стилі CSS, JavaScript-файли, зображення тощо, щоб вони могли бути швидко завантажені без необхідності повторного звернення до сервера.
Я вже декілька разів вказав на те, що Service Worker працює в фоновому режимі та не залежить від поточного контексту. Насправді цей скрипт виконується в окремому, унікальному контексті. Контекст є ізольованим від основного потоку виконання, Service Worker не має доступу до DOM-дерева та виконується повністю асинхронно.
Сучасні вебзастосунки та вебсайти постійно комунікують із сервером, зазвичай синхронні та асинхронні запити. Рідше, але також доволі часто, це комунікація через вебсокети.
Як застосункам, які базуються на постійній роботі з сервером, працювати в офлайн-режимі
Відразу хочу зробити наголос на тому, що в будь-якому разі це буде обмежена робота.
Справа в тому, що окрім кешування даних, Service Workers можуть перехоплювати мережеві запити, модифікувати їх або брати відповідь зі збережених в кеш даних. Здається, що процес роботи досить простий, але є досить багато стратегій кешування даних, які можуть бути досить складними в реалізації.
Хотілося б відзначити механізм фонового оновлення даних. Цей механізм має великий, або навіть критичний вплив на вебзастосунок, який працює офлайн. Тож, як ми вже знаємо, Service Worker не має доступу до багатьох звичних нам API сторінки, оскільки працює в ізольованому контексті, але має доступ до API-кешування (caches) та до API мережевих запитів (fetch). Як ви вже зрозуміли, якраз за допомогою fetch API і відбувається синхронізація даних із сервером, коли зʼявляється підключення до мережі (зʼявляється інтернет).
Що потрібно знати перед роботою с Service Worker
Окрім того, що було написано вище (ізоляція, кешування, контроль мережевих запитів), перед початком роботи з Service Workers потрібно розуміти такі речі:
Життєвий цикл
Реєстрація. Робота з Service Worker завжди починається з реєстрації скрипта Service Worker на сторінці.
Код:
navigator.serviceWorker.register(‘шлях до файлу Service Worker.js’, { scope: ‘/scope/’ })
Також за допомогою другого аргументу функції можна задати сферу дії Service Worker.
Встановлення. Після етапу реєстрації Service Worker переходить на стадію встановлення, під час якої виконується подія install
, зазвичай, саме в цей момент відбувається кешування статичних ресурсів (HTML, CSS, JS і т. д.) потрібних для офлайн-роботи застосунку. Після цього Service Worker переходить до стадії активації.
Активація. Після закінчення події install Service Worker переходить до стадії активації, під час якої відбувається подія activate
. Одне з головних завдань, котре потрібно виконати — це видалення старого кешу, який уже був створений раніше, для вивільнення місця та запобігання конфліктів між різними версіями кешованих даних.
Перехоплення мережевих запитів. Вище ми вже говорили про те, що Service Worker має можливість перехоплювати мережеві запити, модифікувати їх або віддавати кешовані дані. Важливо розуміти, що мовиться як про запити на дані, так і про запити на статичні ресурси: HTML, CSS, JavaScript, медіафайли. Після перехоплення запитів за допомогою caches API актуальні дані можуть бути закешовані. Тобто логіка така: якщо сервер нам відповідає даними, ми можемо їх закешувати, якщо сервер відповідає помилкою або запит на сервер не може бути відправлений через проблеми з мережею, ми можемо дістати з кешу дані, які були туди додані раніше, та використати їх.
Оновлення. Коли вебсторінка завантажується, браузер автоматично перевіряє, чи існує оновлена версія файлу Service Worker за тим самим шляхом. Якщо файл Service Worker був змінений, браузер завантажує його, після чого відбуваються події, які описані вище, а саме: Встановлення та Активація. У цьому разі Service Worker буде оновлено тільки після того, як всі сторінки, які були відкриті та працювали під старою версією Service Worker, будуть закриті. Нагадаю, Service Worker працює в фоновому режимі в окремому контексті, який не пов’язаний з контекстом сторінки, тому процес саме такий. Важливо розуміти, що процес оновлення необхідно ретельно тестувати для того, щоб він спрацював саме так, як вам потрібно.
Видалення. Коли активується нова версія Service Worker, стара версія видаляється, — таким способом відбувається перехід між версіями. Хочу зазначити, що під час видалення старої версії Service Worker, рекомендовано видаляти кеш, пов’язаний з тою версією.
Спілкування з вебсторінками
Хоч, як і було сказано раніше, Service Workers працюють в ізольованому контексті та не мають доступу до DOM-дерева, все ж вони можуть спілкуватися зі сторінкою за допомогою іншого механізму. Цей механізм називається postMessage API. Він не є унікальним для Service Workers, також використовується для реалізації спілкування JavaScript коду з іншими сутностями.
Безпека
Безпека Service Workers ґрунтується на примусовому використанні HTTPS для запобігання перехоплення та модифікації коду та даних. Важливо уважно ставитись до обробки мережевих запитів і відповідей, щоб запобігти вставці шкідливого коду. Також необхідно регулярно оновлювати та перевіряти Service Workers, щоб уникнути зловмисного використання застарілих кешованих даних чи застарілих стратегій кешування.
Налаштування офлайн-роботи
Зверніть увагу, після кожного прикладу я додав посилання на GitHub-репозиторій з повною реалізацією функціоналу.
Реєструємо Service Worker
Для початку нам потрібно зареєструвати наш Service Worker в JavaScript-файлі, який підключений до сторінки.
Назвемо його: main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js')
.then(function(registration) {
// Реєстрація успішна
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// Реєстрація провалилася
console.log('ServiceWorker registration failed: ', err);
});
});
}
Створення Service Worker
Створимо файл service-worker.js, у якому будемо описувати логіку його роботи.
const CACHE_NAME = 'v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/main.js'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
У цьому коді:
- install-подія використовується для відкриття кешу та додавання початкового набору файлів до кешу;
- fetch-подія перехоплює мережеві запити. Якщо запитуваний ресурс перебуває в кеші, він повертається з кешу, інакше він завантажується з мережі.
Важливо розуміти, що це тільки базовий приклад роботи.
Демонстраційні файли ви знайтеде ось тут
Інші можливості
Насправді Service Worker має низку інших можливостей, як-от:
- фонова синхронізація — синхронізація даних, що відбувається, поки застосунок не активний;
- push-повідомлення — можливість відправлення push-повідомлень, коли користувач не перегладає сайт.
- перехоплення та модифікація мережевих запитів — гнучке керування мережевими запитами та генерація відповідей на них.
Розглянемо ці можливості на прикладах.
Фонова синхронізація
У цьому прикладі я надам тільки декілька файлів демонстраційного проєкту, повний проєкт можна знайти в моєму GitHub-репозиторії, посилання буде нижче.
scripts/sync.js — тут міститься логіка для обробки форми та реєстрації фонової синхронізації.
document.getElementById('syncForm').addEventListener('submit', function(event) {
event.preventDefault();
const data = {
message: document.getElementById('dataInput').value
};
// Збереження даних в локальному сховищі
localStorage.setItem('syncData', JSON.stringify(data));
// Реєстрація події синхронізації
if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready
.then(function(swRegistration) {
return swRegistration.sync.register('sync-data');
})
.catch(function() {
// Відправка даних відразу, якщо фонова синхронізація недоступна
sendData(data);
});
} else {
// Фонова синхронізація не підтримується
sendData(data);
}
});
function sendData(data) {
// Код для відправлення даних на сервер
}
service-worker.js — додавання обробника фонової синхронізації до Service Worker.
self.addEventListener('sync', function(event) {
if (event.tag === 'sync-data') {
event.waitUntil(
// Отримання даних з локального сховища
localforage.getItem('syncData').then(function(data) {
return fetch('/path-to-your-api', {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' }
});
}).then(function(response) {
return response.json();
}).then(function(data) {
console.log('Дані успішно синхронізовані:', data);
// Видалення даних з локального сховища після успішної синхронізації
localforage.removeItem('syncData');
}).catch(function(err) {
console.error('Помилка під час синхронізації:', err);
})
);
}
});
Нотатки:
- У цьому прикладі використовується
localforage
для зберігання даних, які потрібно синхронізувати. Вам потрібно буде додати бібліотекуlocalforage
у ваш проєкт. - Функція
sendData
у scripts/sync.js має містити логіку для відправлення даних на сервер.
Повний приклад демонстраційного проєкту доступний за посиланням.
Push-повідомлення
Аналогічно до попереднього прикладу тут будуть тільки основні файли для реалізації push-повідомлень, повну версію проєкту ви знайдете нижче.
scripts/push.js — логіка для підписки на push-повідомлення.
document.getElementById('subscribe').addEventListener('click', function() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(function(registration) {
registration.pushManager.subscribe({
userVisibleOnly: true,
// Використовуйте власний вебключ для push-повідомлень
applicationServerKey: 'YOUR_WEB_PUSH_KEY'
}).then(function(subscription) {
console.log('User is subscribed:', subscription);
}).catch(function(err) {
console.log('Failed to subscribe the user: ', err);
});
});
}
});
service-worker.js — додавання обробника для push-повідомлень
self.addEventListener('push', function(event) {
const options = {
body: 'Це push-повідомлення!',
icon: 'images/icon.png',
badge: 'images/badge.png'
};
event.waitUntil(
self.registration.showNotification('Push Notification', options)
);
});
Нотатки
- Ви маєте згенерувати власний вебключ для push-повідомлень, який буде використовуватися у
applicationServerKey
уscripts/push.js
. - Для використання push-повідомлень вам також потрібно буде налаштувати backend-сервер, який відправлятиме push-повідомлення до підписаних користувачів.
Повний приклад демонстраційного проєкту доступний за посиланням
Перехоплення та модифікація мережевих запитів
Аналогічно до попереднього прикладу тут будуть тільки основні файли для реалізації функціоналу перехоплення та модифікація мережевих запитів, повну версію проєкту ви знайдете нижче.
scripts/main.js — скрипт, що робить запит до сервера
document.addEventListener('DOMContentLoaded', function() {
fetch('/data')
.then(function(response) {
return response.text();
})
.then(function(data) {
document.getElementById('content').innerText = data;
})
.catch(function(err) {
console.error('Помилка:', err);
});
});
service-worker.js — Service Worker, який перехоплює запити.
self.addEventListener('install', function(event) {
self.skipWaiting();
});
self.addEventListener('activate', function(event) {
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', function(event) {
if (event.request.url.endsWith('/data')) {
event.respondWith(
new Response('Це відповідь, змінена Service Worker!')
);
}
});
Повний приклад демонстраційного проєкту доступний за цим посиланням
Цікаве
- Стратегії кешування в Service Workers використовуються для забезпечення швидшого доступу до ресурсів вебзастосунку, зменшення навантаження на сервери та забезпечення роботи додатку при обмеженому або відсутньому інтернет-з’єднанні. Це підвищує продуктивність вебзастосунку та покращує загальний досвід користувача.
- Станом на 2023 рік, Service Workers мають широку підтримку у більшості сучасних веббраузерів. Загальна підтримка Service Workers у всьому світі складає приблизно 97.31%
Висновок
Отже ми досить глибоко занурились в світ Service Workers і, сподіваюсь, ви зрозуміли, наскільки це круті інструменти та чому їх варто використовувати. Ці інструменти дозволяють вашому вебзастосунку чи сайту витримувати випробування офлайн-режимом, уміють працювати з кешем, надсилати push-повідомлення та навіть керувати мережевими запитами.
Знаєте, в сучасному світі, де швидкість і доступність інформації мають велике значення, Service Workers — це справжній порятунок. Маючи їх у вашому арсеналі, ви можете створити застосунок, який не тільки швидко працює та має круту функціональність, але й може витримувати виклики нестабільного інтернет-з’єднання.
На цьому закінчимо, сподіваюсь, інформація була зрозумілою та корисною.
3 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів