Фреймворк Fresh в JavaScript построен на концепции рендеринга на сервере с минимальной нагрузкой на клиент. Несмотря на это, поддержка offline функциональности становится ключевым аспектом для приложений с высокой надежностью и быстродействием. Offline режим позволяет обеспечивать доступ к контенту и функционалу даже при отсутствии соединения с сервером, используя локальные ресурсы и кэширование.
Ключевой инструмент для реализации offline режима — Service Worker. Это отдельный скрипт, работающий в фоне браузера, который перехватывает сетевые запросы, управляет кэшами и может обеспечивать работу приложения без подключения к сети.
Пример регистрации Service Worker во Fresh:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(() => console.log('Service Worker зарегистрирован'))
.catch(err => console.error('Ошибка регистрации Service Worker:', err));
}
Файл sw.js содержит логику кэширования и обработки
запросов:
const CACHE_NAME = 'fresh-app-cache-v1';
const URLS_TO_CACHE = [
'/',
'/styles.css',
'/main.js',
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(URLS_TO_CACHE))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
Ключевые моменты:
install — событие, при котором создается кэш с
необходимыми ресурсами.fetch — перехват запросов к серверу с возвратом
локальной копии из кэша при отсутствии сети.Для приложений Fresh важно кэшировать не только статические файлы, но и ответы API. Используется подход stale-while-revalidate, который возвращает старые данные мгновенно и одновременно обновляет их в фоне.
Пример Service Worker с API кэшированием:
self.addEventListener('fetch', event => {
if (event.request.url.includes('/api/')) {
event.respondWith(
caches.open('api-cache').then(cache =>
cache.match(event.request).then(response =>
fetch(event.request).then(networkResponse => {
cache.put(event.request, networkResponse.clone());
return response || networkResponse;
}).catch(() => response)
)
)
);
}
});
Такой подход обеспечивает минимальную задержку при запросе данных и гарантирует работу при отсутствии сети.
Для хранения сложных данных можно использовать IndexedDB, которая позволяет сохранять объекты с произвольной структурой. В Fresh можно интегрировать IndexedDB для кэширования данных форм, пользовательских настроек или офлайн-сессий.
Пример использования IndexedDB с оберткой idb:
import { openDB } from 'idb';
const dbPromise = openDB('fresh-db', 1, {
upgrade(db) {
db.createObjectStore('posts', { keyPath: 'id' });
},
});
async function savePost(post) {
const db = await dbPromise;
await db.put('posts', post);
}
async function getPost(id) {
const db = await dbPromise;
return await db.get('posts', id);
}
IndexedDB позволяет организовать гибкий доступ к локальным данным и синхронизировать их с сервером при восстановлении соединения.
Во Fresh маршрутизация строится на серверной стороне. Для offline
режима необходимо предусмотреть обработку навигации локально. Service
Worker может возвращать кэшированные HTML-страницы для известных
маршрутов, а динамические маршруты кэшировать через стратегию
Network falling back to cache.
Пример:
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch(() => caches.match('/offline.html'))
);
}
});
offline.html — это резервная страница, отображаемая при
отсутствии сети, что обеспечивает плавный пользовательский
опыт даже при недоступном сервере.
Fresh использует концепцию Islands, где отдельные
компоненты клиентской логики подключаются к статическому HTML. Для
offline работы необходимо, чтобы Islands могли работать с локальными
данными. Это достигается использованием локальных кэшей или IndexedDB
внутри компонентов, а также подпиской на события восстановления сети
(online/offline) для синхронизации данных.
Пример подписки на события сети:
window.addEventListener('online', () => {
console.log('Соединение восстановлено, синхронизация данных');
syncLocalChanges();
});
window.addEventListener('offline', () => {
console.log('Сеть недоступна, работа в офлайн режиме');
});
Для больших приложений важно управлять версионированием кэшей и лимитами хранения:
Cache-Control и ETag для
автоматического обновления ресурсов.activate Service Worker:self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(keyList =>
Promise.all(keyList.map(key => {
if (!cacheWhitelist.includes(key)) {
return caches.delete(key);
}
}))
)
);
});
Offline функциональность может включать Push-уведомления, которые оповещают пользователя о новых данных, даже если приложение закрыто. Fresh интегрируется с Push API через Service Worker:
self.addEventListener('push', event => {
const data = event.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icon.png'
});
});
Push-уведомления обеспечивают асинхронное обновление информации и повышают вовлеченность пользователей.
Такой подход обеспечивает полностью функционирующее приложение в условиях отсутствия сети с возможностью синхронизации данных при восстановлении соединения.