Dirty checking и change tracking

В Backbone.js управление состоянием моделей и отслеживание изменений данных играют ключевую роль для построения реактивных и отзывчивых интерфейсов. Основные механизмы для этого — dirty checking и change tracking, которые позволяют определить, были ли изменения в модели, и реагировать на них соответствующим образом.


Основы модели в Backbone.js

Модель (Backbone.Model) представляет собой структуру данных с набором атрибутов и методами для управления ими. Каждый объект модели хранит свои атрибуты в приватном объекте attributes. Любое изменение атрибутов осуществляется через методы set и unset, а также через конструктор при создании модели.

Пример создания модели:

var Task = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});

var task = new Task({ title: 'Learn Backbone.js' });

Механизм Dirty Checking

Dirty checking — это процесс определения того, изменились ли атрибуты модели по сравнению с их предыдущим состоянием. Backbone реализует это через внутреннюю структуру changedAttributes и события change.

Основные моменты:

  • Метод set принимает объект с новыми значениями атрибутов.
  • Если новое значение отличается от старого, атрибут считается изменённым.
  • Модель сохраняет список изменённых атрибутов в объекте changed.
  • После успешного изменения атрибутов вызывается событие change, а также события для конкретных атрибутов (change:attributeName).

Пример:

task.on('change:title', function(model, value) {
  console.log('Title changed to:', value);
});

task.set({ title: 'Master Backbone.js' });
// Выведет: Title changed to: Master Backbone.js

Метод hasChanged позволяет проверить, были ли изменения в модели с момента последнего сохранения:

task.hasChanged();           // true
task.hasChanged('title');    // true
task.changedAttributes();    // { title: 'Master Backbone.js' }

Change Tracking

Change tracking — более детализированное отслеживание изменений атрибутов. Backbone предоставляет API для работы с предыдущими и текущими значениями:

  • previous(attribute) возвращает значение атрибута до последнего изменения.
  • previousAttributes() возвращает объект со всеми предыдущими атрибутами модели.

Пример:

console.log(task.previous('title'));         // 'Learn Backbone.js'
console.log(task.previousAttributes());      
// { title: 'Learn Backbone.js', completed: false }

Это позволяет реализовывать сложные сценарии, например, откат изменений или условное реагирование на изменения только в определённых атрибутах.


События и реактивность

Backbone.js использует событийную модель для оповещения об изменениях:

  • change — вызывается при любом изменении модели.
  • change:attributeName — вызывается при изменении конкретного атрибута.
  • sync — вызывается после успешного сохранения модели на сервере.
  • invalid — вызывается, если модель не прошла валидацию.

Пример подписки на несколько событий:

task.on({
  'change:title': function(model, value) {
    console.log('Title changed:', value);
  },
  'change:completed': function(model, value) {
    console.log('Completed changed:', value);
  },
  'change': function() {
    console.log('Some attribute changed');
  }
});

Работа с коллекциями

Коллекции (Backbone.Collection) также поддерживают отслеживание изменений через события моделей:

  • update — вызывается при добавлении, удалении или изменении моделей в коллекции.
  • change — пробрасывается от модели к коллекции.

Пример:

var Tasks = Backbone.Collection.extend({
  model: Task
});

var tasks = new Tasks([task]);

tasks.on('change', function(model) {
  console.log('A task has changed:', model.toJSON());
});

task.set({ completed: true });
// Выведет: A task has changed: { title: 'Master Backbone.js', completed: true }

Оптимизация Dirty Checking

При работе с большими коллекциями или моделями с множеством атрибутов важно минимизировать лишние события:

  • Использовать метод silent: true при массовых изменениях, чтобы отложить события.
  • Вызвать model.trigger('change') вручную после завершения изменений.
  • Применять listenTo вместо on для автоматического снятия подписок при уничтожении объектов.

Пример массового обновления с минимизацией событий:

task.set({ title: 'Finalize Backbone.js', completed: true }, { silent: true });
task.trigger('change'); // одно событие вместо двух

Заключение по функциональности

Dirty checking и change tracking обеспечивают Backbone.js простой, но мощный способ отслеживания изменений в данных. Они формируют основу реактивной архитектуры, позволяя моделям и коллекциям уведомлять представления о любых изменениях и обеспечивать синхронизацию состояния интерфейса с данными. Понимание этих механизмов критично для построения масштабируемых веб-приложений с Backbone.js.