Offline functionality

Mithril.js предоставляет лёгкий и гибкий подход к построению одностраничных приложений, при этом поддержка оффлайн-функциональности требует понимания нескольких ключевых механизмов браузера: Service Workers, IndexedDB, LocalStorage и событий сети. Реализация оффлайн-режима в Mithril строится вокруг управления состоянием приложения и синхронизации данных между клиентом и сервером при восстановлении соединения.


Использование Service Workers

Service Workers — скрипты, работающие в фоновом режиме, которые позволяют перехватывать сетевые запросы и кэшировать ресурсы для работы приложения без подключения к интернету. Основные шаги интеграции:

  1. Регистрация Service Worker:
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js')
        .then(reg => console.log('Service Worker зарегистрирован', reg))
        .catch(err => console.error('Ошибка регистрации SW:', err));
}
  1. Кэширование ресурсов внутри SW:
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))
    );
});
  1. Обработка fetch-запросов:
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-компоненты и оффлайн

В компонентах 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 позволяет подгружать данные как из сети, так и из локального хранилища, что обеспечивает прозрачную работу приложения оффлайн.


Оптимизация оффлайн-режима

  • Кэширование динамических данных: через IndexedDB с механикой очереди изменений для последующей синхронизации.
  • События сети: отслеживать online и offline для обновления интерфейса и уведомления пользователя.
  • Версионирование кэша: обновление Service Worker при изменении ресурсов, чтобы избежать устаревших данных.
  • Lazy-loading данных: загружать данные только при необходимости, что уменьшает нагрузку на память и увеличивает скорость работы оффлайн.

Эта архитектура позволяет Mithril-приложениям быть полностью функциональными без постоянного подключения к сети, при этом сохраняется реактивность компонентов и интеграция с современными возможностями браузеров.