Mithril.js предоставляет лёгкий и гибкий подход к построению одностраничных приложений, при этом поддержка оффлайн-функциональности требует понимания нескольких ключевых механизмов браузера: Service Workers, IndexedDB, LocalStorage и событий сети. Реализация оффлайн-режима в Mithril строится вокруг управления состоянием приложения и синхронизации данных между клиентом и сервером при восстановлении соединения.
Service Workers — скрипты, работающие в фоновом режиме, которые позволяют перехватывать сетевые запросы и кэшировать ресурсы для работы приложения без подключения к интернету. Основные шаги интеграции:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('Service Worker зарегистрирован', reg))
.catch(err => console.error('Ошибка регистрации SW:', err));
}
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/bundle.js',
'/styles.css'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
Таким образом, приложение получает возможность работать с заранее закэшированными ресурсами, даже при отсутствии сети.
Для оффлайн-приложений важно хранить данные локально. Mithril не предоставляет встроенной системы хранения, но легко интегрируется с IndexedDB и LocalStorage.
LocalStorage подходит для небольших объёмов данных и синхронизации при простых сценариях:
// Сохранение состояния модели
localStorage.setItem('tasks', JSON.stringify(TaskModel.list));
// Загрузка состояния при старте приложения
TaskModel.list = JSON.parse(localStorage.getItem('tasks')) || [];
IndexedDB обеспечивает более сложное хранение с поддержкой транзакций и больших объёмов данных:
const request = indexedDB.open('myDB', 1);
request.onupgradenee ded = event => {
const db = event.target.result;
db.createObjectStore('tasks', { keyPath: 'id' });
};
request.onsucc ess = event => {
const db = event.target.result;
const tx = db.transaction('tasks', 'readwrite');
const store = tx.objectStore('tasks');
store.put({ id: 1, title: 'Learn Mithril', done: false });
tx.oncompl ete = () => db.close();
};
Ключевым элементом является отслеживание состояния сети и синхронизация данных с сервером:
m.request = function(options) {
if (!navigator.onLine) {
console.warn('Нет соединения. Данные будут сохранены локально.');
saveOffline(options); // пользовательская функция для сохранения запроса
return Promise.resolve({ offline: true });
}
return m.request(options);
};
window.addEventListener('online', () => {
console.log('Соединение восстановлено. Синхронизация...');
syncOfflineData(); // функция для отправки сохранённых оффлайн-запросов на сервер
});
Такой подход обеспечивает непрерывную работу интерфейса, даже если сервер временно недоступен.
В компонентах Mithril можно интегрировать оффлайн-логику напрямую через lifecycle methods:
const TaskList = {
oninit: vnode => {
if (navigator.onLine) {
m.request({ method: 'GET', url: '/api/tasks' })
.then(data => vnode.state.tasks = data);
} else {
vnode.state.tasks = loadOfflineTasks(); // функция загрузки из IndexedDB
}
},
view: vnode => m('ul', vnode.state.tasks.map(task =>
m('li', task.title)
))
};
Использование oninit позволяет подгружать данные как из
сети, так и из локального хранилища, что обеспечивает прозрачную работу
приложения оффлайн.
online и
offline для обновления интерфейса и уведомления
пользователя.Эта архитектура позволяет Mithril-приложениям быть полностью функциональными без постоянного подключения к сети, при этом сохраняется реактивность компонентов и интеграция с современными возможностями браузеров.