О насБлогКонтакты
Frontend разработка19 марта 2013 г. 3 мин 104Обновлено: 22 июня 2026 г.

Эпоха Backbone.js и Knockout.js: до того, как React и Angular захватили мир

AunimedaAunimeda
📋 Содержание

React вышел на JSConf в мае 2013. AngularJS 1.0 появился в июне 2012, но не получил массовой аудитории до 2013. До того как эти фреймворки стали доминировать, команды, строящие сложные JavaScript-приложения, имели два серьёзных варианта: Backbone.js (октябрь 2010) и Knockout.js (июль 2010).

Мы использовали оба. Они решали разные задачи, имели разную философию и давали разный код.


Backbone.js: принеси свою структуру

Джереми Ашкенас построил Backbone на одной идее: JavaScript нуждался в минимальной структуре MVC без навязанных мнений фреймворка. Модели, Коллекции, Представления и Router - всё остальное решаешь сам.

// Backbone 0.9.x - модель Product
var Product = Backbone.Model.extend({
  defaults: { name: '', price: 0, inStock: true },
  urlRoot: '/api/products',
  
  validate: function(attrs) {
    if (!attrs.name || attrs.name.trim() === '') return 'Имя обязательно';
    if (attrs.price < 0) return 'Цена не может быть отрицательной';
  },
  
  formattedPrice: function() {
    return this.get('price').toFixed(2) + ' сом';
  }
});

View-слой в Backbone был болезненным. Вы отвечали за:

  • Рендеринг HTML
  • Привязку событий
  • Обновление DOM при изменении модели
  • Очистку слушателей событий для предотвращения утечек памяти
var ProductView = Backbone.View.extend({
  events: {
    'click .btn-add-to-cart': 'onAddToCart',
  },
  
  initialize: function() {
    this.listenTo(this.model, 'change', this.render);
    this.listenTo(this.model, 'destroy', this.remove);
  },
  
  render: function() {
    var template = _.template(
      '<h3><%= name %></h3><p><%= formattedPrice() %></p>' +
      '<% if (inStock) { %><button class="btn-add-to-cart">В корзину</button><% } %>'
    );
    this.$el.html(template(this.model.toJSON()));
    return this;
  },
  
  // КРИТИЧНО: удаляем слушатели для предотвращения утечек памяти
  remove: function() {
    this.stopListening();
    Backbone.View.prototype.remove.call(this);
  }
});

Проблема утечки памяти была реальной. View, слушающий события модели через model.on('change', this.render, this) без последующего model.off, удерживал модель, ссылающуюся на View, а View удерживал свой DOM-элемент - даже после удаления элемента со страницы.


Knockout.js: привязка данных без MVC

Knockout взял противоположную философию - двусторонняя привязка данных через «наблюдаемые переменные»:

function ProductViewModel(data) {
  var self = this;
  
  self.name = ko.observable(data.name || '');
  self.price = ko.observable(data.price || 0);
  
  // Вычисляемое наблюдаемое - пересчитывается автоматически
  self.formattedPrice = ko.computed(function() {
    return self.price().toFixed(2) + ' сом';
  });
  
  self.addToCart = function() {
    cart.add(self, 1);
  };
}
<div data-bind="with: product">
  <h3 data-bind="text: name"></h3>
  <p data-bind="text: formattedPrice"></p>
  <!-- ko if: inStock() -->
    <button data-bind="click: addToCart">В корзину</button>
  <!-- /ko -->
</div>

Когда price менялась, formattedPrice автоматически пересчитывалась, и DOM автоматически обновлялся. Никакой ручной работы с DOM. Никаких слушателей событий для управления.


Ключевые различия

Функция Backbone.js Knockout.js
Философия Минимальная структура Магия привязки данных
Обновление DOM Ручное, в render() Автоматическое через наблюдаемые
Отладка Легко (обычный JS) Сложно (граф зависимостей)
Лучше подходит Сложные кастомные UI Формы, CRUD

Наши ошибки

Backbone: мы не соблюдали границы компонентов. View разрастался до 400 строк. Логика модели накапливалась. Гибкость, делающая Backbone простым в начале, делала его трудным в поддержке.

Knockout: цепочки наблюдаемых было сложно отлаживать. Вычисляемое наблюдаемое, зависящее от 5 других наблюдаемых, каждое из которых зависит от ещё 3, создавало граф зависимостей, который никто не мог удержать в голове.


Наследие

React появился в 2013 с виртуальным DOM и однонаправленным потоком данных. Он сделал ручное управление DOM в Backbone и магию наблюдаемых Knockout похожими на обходные пути для одной и той же проблемы: мутировать DOM трудно понимать. Ответ React - не мутировать DOM, описывать его желаемый вид, позволить фреймворку вычислить diff - был правильным ответом.

К 2015 году мы всё писали на React. К 2016 переносили старые Backbone-кодовые репозитории на React. К 2018 переписали каждый значимый Knockout-проект.

Но Backbone научил нас структуре и событийно-ориентированной архитектуре. Knockout научил реактивности и декларативному UI. Концепции выжили, хотя реализации были отправлены на покой.

Читайте также

React против Angular 2: почему мы выбрали React для CRM-системыaunimeda
Frontend разработка

React против Angular 2: почему мы выбрали React для CRM-системы

В 2016 году мы две недели сравнивали React + Redux и Angular 2 для сложной CRM. Честный разбор: двустороннее связывание против однонаправленного потока данных, и что действительно важно при масштабировании.

Ранние одностраничные приложения: управление состоянием до появления Reduxaunimeda
Frontend разработка

Ранние одностраничные приложения: управление состоянием до появления Redux

Redux вышел в 2015. До него мы управляли состоянием SPA через кастомные event bus, хеши URL, localStorage и молитву. Вот как выглядело управление состоянием в 2012-2014.

React vs Vue vs Angular в 2026: что выбрать для проекта в Бишкекеaunimeda
Frontend разработка

React vs Vue vs Angular в 2026: что выбрать для проекта в Бишкеке

Честное сравнение трёх главных фреймворков от разработчика, работающего с ними ежедневно: React 19, Vue 3.5, Angular 19. Когда что брать, реальные примеры и рынок Кыргызстана.

Нужна IT-разработка для вашего бизнеса?

Разрабатываем сайты, мобильные приложения и AI-решения для бизнеса в Кыргызстане. Бесплатная консультация.

Разработка сайтов

Получить консультацию Все статьи