Backbone.js изначально проектировался как тонкий MVC-фреймворк для браузера с опорой на REST-синхронизацию. Однако архитектура Backbone допускает расширение и переопределение механизмов синхронизации, что делает возможной полноценную offline-first стратегию.
Ключевым элементом является метод Backbone.sync, который
отвечает за обмен данными между моделями/коллекциями и сервером. Любая
offline-функциональность в Backbone строится вокруг кастомизации этого
метода и использования локальных хранилищ браузера.
Основные варианты локального хранения:
localStoragesessionStorageНа практике чаще всего используется localStorage или
IndexedDB, так как они не требуют дополнительных разрешений и
поддерживаются большинством браузеров.
Backbone.sync принимает пять параметров:
Backbone.sync = function(method, model, options) {
// method: 'create', 'read', 'update', 'delete'
// model: Backbone.Model или Backbone.Collection
// options: success, error и др.
};
Для offline-режима стратегия обычно делится на два уровня:
Простейший пример подмены sync для
localStorage:
Backbone.sync = function(method, model, options) {
var key = model.urlRoot || model.url;
var data = JSON.parse(localStorage.getItem(key)) || [];
switch (method) {
case 'read':
options.success(data);
break;
case 'create':
data.push(model.toJSON());
localStorage.setItem(key, JSON.stringify(data));
options.success(model.toJSON());
break;
}
};
Такой подход демонстрирует принцип, но не решает задачи идентификации моделей, конфликтов и очередей изменений.
Популярным решением является использование плагина
Backbone.LocalStorage, который реализует полноценный
storage-адаптер.
Пример подключения:
var Todo = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
var Todos = Backbone.Collection.extend({
model: Todo,
localStorage: new Backbone.LocalStorage('todos-backbone')
});
Плагин автоматически переопределяет sync для конкретной
коллекции или модели, сохраняя данные в localStorage с
поддержкой CRUD-операций.
Преимущества подхода:
Ограничения:
localStorageOffline-first стратегия предполагает, что приложение корректно работает без сети, а сервер используется как механизм синхронизации, а не как единственный источник истины.
Основные принципы:
В Backbone это реализуется через разделение логики:
Model — хранит состояние и флаги синхронизацииCollection — управляет очередями измененийSync layer — решает, когда и как отправлять данныеДля поддержки offline-режима необходимо хранить историю операций, которые ещё не были отправлены на сервер.
Пример структуры очереди:
[
{ "method": "create", "model": {...}, "timestamp": 123456 },
{ "method": "update", "model": {...}, "timestamp": 123789 }
]
Очередь может храниться в localStorage или IndexedDB.
Backbone-модели дополняются метаданными:
var Task = Backbone.Model.extend({
defaults: {
synced: false,
updatedAt: null
}
});
При каждом save в offline-режиме:
synced: falseСамая простая стратегия. Последнее изменение перезаписывает предыдущие.
Особенности:
Реализация обычно основана на поле updatedAt.
Выбор стратегии зависит от бизнес-логики. В Backbone это реализуется
на уровне обработки ответа сервера в success колбэке.
Каждая модель содержит версию:
defaults: {
version: 1
}
При синхронизации сервер сравнивает версии и возвращает конфликт, если данные устарели.
Backbone позволяет перехватывать такие ответы и вручную разрешать конфликт, например, через:
model.fetch({
error: function(model, response) {
if (response.status === 409) {
// конфликт версий
}
}
});
Часто используется комбинированный sync, который:
Пример архитектурного паттерна:
var originalSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
localSync(method, model, options);
if (navigator.onLine) {
return originalSync(method, model, {
success: function(resp) {
markAsSynced(model);
options.success(resp);
}
});
}
};
Такой подход позволяет сохранить стандартный REST-механизм Backbone, добавив offline-уровень.
localStorage синхронен и ограничен по объёму. Для
сложных приложений предпочтительнее IndexedDB.
Основные преимущества:
В Backbone IndexedDB обычно используется через адаптеры или кастомный
sync.
Пример концепции:
function indexedDBSync(method, model, options) {
// асинхронная работа с IndexedDB
}
Такой sync может работать параллельно с сетевым,
формируя надёжную offline-first модель.
Для корректной синхронизации важно отслеживать доступность сети:
window.addEventListener('online', syncQueue);
window.addEventListener('offline', markOffline);
Backbone-приложение может хранить глобальное состояние:
var AppState = new Backbone.Model({
online: navigator.onLine
});
Это состояние используется для:
Backbone.Views должны учитывать offline-статус модели:
Типичный паттерн:
model.on('change:synced', this.render, this);
Таким образом UI остаётся реактивным и не зависит от немедленного ответа сервера.
Backbone.js, несмотря на возраст, предоставляет достаточно гибкие примитивы для реализации сложных offline-first стратегий без нарушения основной архитектуры фреймворка.