Создание современной PWA: Опыт Callisto
Введение
Привет, друзья! Сегодня я расскажу, как мы создали Callisto — прогрессивное веб-приложение (PWA) с использованием Workbox, Webpack и Service Worker. Мы разберем, почему мы выбрали PWA, как его построили и какие уроки вынесли из этого опыта.
Почему PWA?
Перед тем как погрузиться в технические детали, давайте разберем, почему мы решили создать PWA. Традиционные веб-приложения имеют ряд ограничений: отсутствие офлайн-доступа, медленная загрузка и зависимость от стабильного интернет-соединения. В отличие от них, PWA обеспечивает опыт, схожий с нативными приложениями, предлагая такие возможности, как:
-
Поддержка офлайн-режима – ваше приложение работает даже без интернета.
-
Быстрая загрузка – благодаря стратегиям кэширования.
-
Push-уведомления – помогают удерживать пользователей.
-
Устанавливаемость – пользователи могут добавить приложение на домашний экран.
Для нашего проекта Callisto, который связан с управлением заявками для техников, офлайн-функциональность была критически важна, так как техники часто работают в зонах с плохим интернетом.
Как мы это сделали
Мы построили Callisto на основе трех ключевых технологий:
-
Webpack для сборки и оптимизации ресурсов.
-
Workbox для упрощения работы с Service Worker.
-
Web App Manifest для обеспечения установки приложения.
Шаг 1: Настройка Webpack
Мы использовали Webpack для генерации оптимизированных файлов. Важную роль здесь сыграл Workbox-плагин.
const WorkboxPlugin = require('workbox-webpack-plugin');
module.exports = {
plugins: [
new WorkboxPlugin.InjectManifest({
swSrc: './webpack/service-worker.js',
swDest: 'service-worker.js',
}),
],
};
Шаг 2: Реализация Service Worker
Service Worker — это сердце PWA. Он управляет кэшированием и сетевыми запросами.
import { precacheAndRoute } from "workbox-precaching";
import { NetworkFirst } from "workbox-strategies";
const CACHE_NAME = `callisto`;
precacheAndRoute(self.__WB_MANIFEST);
const networkFirst = new NetworkFirst({
cacheName: CACHE_NAME,
});
Шаг 3: Обработка офлайн-сценариев
Что делать, если пользователь запрашивает динамический контент в офлайне? Мы реализовали механизм резервного ответа.
function useFallback() {
return caches.open(CACHE_NAME).then((cache) =>
cache.match('/').then((matching) => {
return matching || new Response('OFFLINE', { headers: { 'Content-Type': 'text/html; charset=utf-8' } });
})
);
}


Шаг 4: Добавление push-уведомлений
Push-уведомления помогают уведомлять пользователей об изменениях в заявках.
self.addEventListener('push', event => {
const data = event.data ? event.data.json() : {};
const title = data.title || 'Новое уведомление';
const options = { body: data.body || 'У вас новое обновление!' };
event.waitUntil(self.registration.showNotification(title, options));
});
Шаг 5: Создание Web App Manifest
Для установки приложения мы определили Web App Manifest:
{
"name": "Callisto FTO",
"short_name": "Callisto",
"start_url": "./",
"display": "standalone",
"icons": [
{ "src": "./icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" }
]
}
Проверка подключения к VPN через Service Worker
Одна из проблем, с которой мы столкнулись, — это необходимость проверки подключения к VPN. Мы решили это через Service Worker, используя периодическую синхронизацию.
self.addEventListener('periodicSync', event => {
if (event.tag === 'vpn-check') {
checkVpnStatus();
}
});
/*
API_URL:
https://api.ipify.org/?format=json
*/
async function checkVpnStatus() {
try {
const response = await fetch('API_URL');
const data = await response.json();
const currentIp = data.ip;
if (currentIp !== 'EXPECTED_VPN_IP') {
sendNotification('VPN подключение потеряно', 'Похоже, что ваше VPN соединение отвалилось.');
}
} catch (error) {
sendNotification('Ошибка подключения', 'Не удалось проверить ваше VPN соединение.');
}
}
Отправка уведомлений
function sendNotification(title, message) {
if (Notification.permission === 'granted') {
self.registration.showNotification(title, { body: message, tag: 'vpn-status' });
}
}
Вызов окна установки приложения
Для того чтобы пользователи могли легко установить PWA, мы добавили обработку события beforeinstallprompt.
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
let deferredPrompt = event;
const installButton = document.getElementById('install-button');
installButton.style.display = 'block';
installButton.addEventListener('click', () => {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the install prompt');
} else {
console.log('User dismissed the install prompt');
}
deferredPrompt = null;
});
});
});
Итог
После внедрения всех этих технологий Callisto стал быстрым, офлайн-доступным, устанавливаемым приложением, обеспечивающим удобный пользовательский опыт для техников.
Основные выводы:
-
Workbox упрощает работу с Service Worker.
-
Грамотное кеширование повышает производительность.
-
PWA объединяют преимущества веба и нативных приложений.
Заключение
Если вы создаете веб-приложение, подумайте о внедрении PWA. Это современный, быстрый и надежный способ улучшить пользовательский опыт.
Спасибо! Удачного кодинга!