Создание и использование observable

Observable в Knockout.js — это основной механизм реактивности, позволяющий отслеживать изменения значений и автоматически обновлять связанные элементы пользовательского интерфейса. Observable представляет собой функцию-обёртку вокруг значения, обеспечивая его динамическое обновление.

var name = ko.observable("Иван");
console.log(name()); // Получение текущего значения
name("Пётр"); // Установка нового значения
console.log(name()); // "Пётр"

Ключевые особенности observable:

  • Доступ через вызов функции: чтобы прочитать значение, вызывают observable(), чтобы записать — observable(новое_значение).
  • Автоматическое уведомление зависимостей: все привязки и computed-функции, использующие это значение, обновляются при его изменении.
  • Может содержать любые типы данных: строки, числа, объекты, массивы.

ObservableArray

Для работы с коллекциями используется observableArray. Она расширяет функциональность обычного observable и предоставляет методы для управления массивом с автоматическим уведомлением зависимостей.

var items = ko.observableArray([1, 2, 3]);
items.push(4);       // Добавление элемента
items.remove(2);     // Удаление элемента
items()[0];          // Получение элемента по индексу

Методы push, pop, shift, unshift, splice, removeAll и другие позволяют удобно модифицировать массив, при этом UI будет синхронизирован автоматически.


Computed и зависимость от observable

Computed — это производная величина, значение которой вычисляется на основе других observable. Computed автоматически пересчитывается при изменении любой из зависимостей.

var firstName = ko.observable("Иван");
var lastName = ko.observable("Иванов");

var fullName = ko.computed(function() {
    return firstName() + " " + lastName();
});

console.log(fullName()); // "Иван Иванов"
firstName("Пётр");
console.log(fullName()); // "Пётр Иванов"

Особенности computed:

  • Автоматическое отслеживание зависимостей.
  • Может быть только для чтения или с возможностью записи (writeable computed).
  • Эффективен для вычислений, которые зависят от нескольких observable.

Подписки на изменения

Observable позволяет подписываться на изменения значения с помощью метода subscribe. Это полезно для выполнения побочных эффектов при изменении данных.

var age = ko.observable(25);
age.subscribe(function(newValue) {
    console.log("Возраст изменился на " + newValue);
});

age(26); // Лог: Возраст изменился на 26

Важные моменты подписки:

  • Подписка получает новое значение в качестве аргумента.
  • Можно создать несколько подписчиков на один observable.
  • Подписку можно отменить вызовом dispose() на возвращаемом объекте.

Привязка observable к UI

Knockout.js использует data-bind для связывания observable с элементами DOM. Наиболее распространённые привязки:

  • text — обновляет текстовое содержимое элемента.
  • value — привязывает значение input.
  • visible — управляет видимостью элемента.
  • foreach — создаёт повторяющиеся элементы для массивов.
<div data-bind="text: name"></div>
<input data-bind="value: name" />

Изменение значения observable в JavaScript автоматически обновляет UI, и наоборот, при изменении input — обновляется observable.


Примеры комплексного использования

Модель с observable и observableArray:

function AppViewModel() {
    this.tasks = ko.observableArray([
        { title: ko.observable("Сделать уроки"), done: ko.observable(false) },
        { title: ko.observable("Прогуляться"), done: ko.observable(true) }
    ]);

    this.completedTasks = ko.computed(function() {
        return this.tasks().filter(task => task.done());
    }, this);

    this.addTask = function(title) {
        this.tasks.push({ title: ko.observable(title), done: ko.observable(false) });
    }.bind(this);
}

ko.applyBindings(new AppViewModel());

Этот пример демонстрирует:

  • Создание массива задач с observable-полями.
  • Вычисление завершённых задач с помощью computed.
  • Добавление новых задач с синхронизацией UI.

Продвинутые возможности

  • Chaining и mapping: можно создавать observable внутри observable или преобразовывать массивы через ko.utils.arrayMap.
  • Rate limiting: computed можно ограничить частоту пересчёта с помощью rateLimit, что полезно при больших объёмах данных.
  • Deferred updates: возможность откладывать обновления observable для повышения производительности.
var searchQuery = ko.observable("").extend({ rateLimit: 500 });
searchQuery.subscribe(function(query) {
    console.log("Поиск: " + query);
});

Такой подход снижает количество лишних вычислений при быстром вводе текста пользователем.


Особенности работы с объектами и массивами

  • Внутри observable можно хранить сложные объекты, но для реактивности вложенных свойств нужно использовать observable для каждого поля.
  • Для массивов внутри объектов удобнее использовать observableArray.
  • Для массовых изменений массива можно использовать push.apply или методы replace и removeAll для минимизации перерисовки интерфейса.

Использование observable и observableArray обеспечивает мощную реактивность в приложениях на Knockout.js, позволяя строить динамичные интерфейсы с минимальным количеством ручного кода для обновления UI.