Offline-функциональность является важной частью современных веб-приложений. Она позволяет пользователю работать с приложением даже при отсутствии подключения к сети и синхронизировать данные при повторном подключении. В Riot.js это достигается сочетанием возможностей фреймворка для управления состоянием компонентов и встроенных веб-технологий, таких как Service Workers и IndexedDB.
Service Workers обеспечивают перехват сетевых запросов и кэширование ресурсов, что является ключевым для оффлайн-доступа.
// Регистрация Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker зарегистрирован с областью:', registration.scope);
})
.catch(error => {
console.error('Ошибка регистрации Service Worker:', error);
});
}
sw.js — основной скрипт Service Worker:
const CACHE_NAME = 'riot-app-cache-v1';
const URLS_TO_CACHE = [
'/',
'/index.html',
'/bundle.js',
'/style.css'
];
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 — перехватывает запросы, возвращая кэшированные
версии при оффлайн-доступе.activate и очистку
старых версий.Для динамических данных используется IndexedDB, так как локальныйStorage ограничен по объему и не подходит для сложных структур.
let db;
const request = indexedDB.open('riotDB', 1);
request.onupgradenee ded = event => {
db = event.target.result;
db.createObjectStore('tasks', { keyPath: 'id', autoIncrement: true });
};
request.onsucc ess = event => {
db = event.target.result;
};
function saveTask(task) {
const transaction = db.transaction(['tasks'], 'readwrite');
const store = transaction.objectStore('tasks');
store.add(task);
}
function getTasks(callback) {
const transaction = db.transaction(['tasks'], 'readonly');
const store = transaction.objectStore('tasks');
const request = store.getAll();
request.onsucc ess = () => callback(request.result);
}
Особенности работы:
transactions обеспечивает целостность
данных.Компоненты Riot.js обладают реактивной системой, что позволяет автоматически обновлять интерфейс при изменении данных, даже при оффлайн-режиме.
<task-list>
<ul>
<li each={task in tasks}>{task.name}</li>
</ul>
this.on('mount', () => {
getTasks(fetchedTasks => {
this.tasks = fetchedTasks;
this.update();
});
});
</task-list>
Ключевые моменты:
each используется для итерации по массиву данных и
автоматического рендеринга списка.update() — метод, вызывающий перерендеринг компонента
при изменении состояния.После восстановления соединения данные можно синхронизировать с сервером, используя простую очередь изменений.
let offlineQueue = [];
function enqueueTask(task) {
offlineQueue.push(task);
saveTask(task);
}
function syncTasks() {
if (navigator.onLine) {
while (offlineQueue.length) {
const task = offlineQueue.shift();
fetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(task)
});
}
}
}
window.addEventListener('online', syncTasks);
Особенности:
offlineQueue хранит задачи, добавленные
оффлайн.online автоматически инициируется
синхронизация.Можно кэшировать ответы API с помощью Service Workers для ускорения повторного доступа:
self.addEventListener('fetch', event => {
if (event.request.url.includes('/api/')) {
event.respondWith(
caches.open('api-cache').then(cache =>
fetch(event.request)
.then(response => {
cache.put(event.request, response.clone());
return response;
})
.catch(() => cache.match(event.request))
)
);
}
});
Важные моменты:
Для улучшения UX важно информировать компоненты о состоянии подключения:
this.onl ine = navigator.onLine;
window.addEventListener('online', () => {
this.onl ine = true;
this.update();
});
window.addEventListener('offline', () => {
this.onl ine = false;
this.update();
});
online может использоваться для условного
отображения кнопок синхронизации или предупреждений.update() гарантирует
реактивное изменение интерфейса.Эта структура позволяет создавать полнофункциональные оффлайн-приложения на Riot.js, обеспечивая устойчивую работу интерфейса и надежное хранение данных при любых условиях сети.