- 1.Вступление
- 2.Проблема
- 3.Как это работает, или оффлайн-работа
- 4.Как всё это может работать вместе?
- 5.Что нужно знать перед работой с Service Worker?
- 6.Общение с веб-страницами
- 7.Безопасность
- 8.Настройки оффлайн-работы
- 9.Другие возможности
- 10.Фоновая синхронизация
- 11.Push-сообщения
- 12.Перехват и модификация сетевых запросов
- 13.Вывод
Слышали ли вы когда-нибудь об оффлайн веб-приложениях или оффлайн вебсайтах? Я уверен что да, а если не слышали, то сейчас ещё узнаете, как и почему это работает и кому нужно.
В названии этой статьи присутствует название технологии, о которой будет идти речь дальше, то есть Service Workers, но перед тем, как мы погрузимся в подробности реализации и использования, выясним, какую проблему решает именно эта технология.
ПРОБЛЕМА
Представим себе ситуацию, на самом деле на момент написания этой статьи я как раз нахожусь в такой ситуации, поэтому это первое, что мне пришло в голову. Итак, вы пишете статью о Service Workers, а для написания используете сервис Google Docs, это ведь так удобно.
При написании текста в Google Docs вы понимаете, что у вас исчезло подключение к интернету, причины могут быть разные, но ситуация такова. Вы уже начинаете волноваться, что нужно быстро сохранить текст куда-нибудь в другое место, чтобы не потерять его. И случайно перезагружаете страницу и думаете: «Ну всё, вся работа потеряна». Но к вашему удивлению, страница перезагружается, весь текст, который вы писали, остаётся на своём месте и вы даже можете продолжить свою работу дальше, ведь всё работает. Непонятно как, но работает.
КАК ЭТО РАБОТАЕТ, ИЛИ ОФФЛАЙН-РАБОТА
Оффлайн работа веб-приложения обычно реализуется через такой инструмент как Service Worker.
Service Worker — это специальный скрипт, работающий в фоновом режиме и не зависящий от страницы, на которой находится пользователь. Это фоновый скрипт, поэтому он может работать и выполнять операции и тогда, когда пользователь не просматривает веб-сайт.
В целом оффлайн работа сайта в любом случае будет базироваться на кэшировании ресурсов и контента сайта: это одна из задач, выполняемых Service Worker. Есть и другие, а именно: пуш-нотификации и обновление данных в фоновом режиме (независимо от страницы или контекста).
Кэширование — процесс хранения копий данных во временном хранилище, известном как кэш. Этот процесс позволяет быстро получить доступ к этим данным при следующих запросах. В контексте веб-разработки кэширование обычно используется для хранения веб-ресурсов, таких как HTML-страницы, стили CSS, JavaScript-файлы, изображения и т. д., чтобы они могли быть быстро загружены без необходимости повторного обращения к серверу.
Я уже несколько раз указал, что Service Worker работает в фоновом режиме и не зависит от текущего контекста. На самом деле, этот скрипт выполняется в отдельном, уникальном контексте. Контекст, в котором выполняется 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 и т. д.), нужных для оффлайн-работы приложения.
Активация. После окончания события 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. Надеюсь, вы поняли, насколько это крутой инструмент и почему его следует использовать. Эти инструменты позволяют вашему веб-приложению или сайту выдерживать испытания оффлайн-режимом, умеют работать с кэшем, отправлять пуш-сообщения и даже управлять сетевыми запросами.
Знаете, в современном мире, где скорость и доступность информации имеют большое значение, Service Workers — это настоящее спасение. Имея их в вашем арсенале, вы можете создать приложение, которое не только быстро работает и имеет крутую функциональность, но может выдерживать вызовы нестабильного интернет-соединения.
На этом закончим, надеюсь, информация была понятной и полезной.