Knockout.js — это легковесный JavaScript-фреймворк, обеспечивающий реализацию паттерна Model-View-ViewModel (MVVM). Основная цель — автоматическая синхронизация данных между моделью и представлением, что снижает необходимость ручного управления DOM.
Observable — это объект, который хранит значение и уведомляет подписчиков при его изменении:
var firstName = ko.observable('Иван');
firstName.subscribe(function(newValue) {
console.log('Имя изменилось на: ' + newValue);
});
firstName('Пётр'); // Лог: Имя изменилось на: Пётр
ObservableArray используется для массивов данных, обеспечивая реактивное обновление представления при изменении элементов:
var items = ko.observableArray(['яблоко', 'банан']);
items.push('груша');
items.remove('банан');
Knockout.js использует привязки для связывания данных модели с элементами DOM. Основные типы привязок:
<span data-bind="text: firstName"></span>
<input data-bind="value: firstName" />
<ul data-bind="foreach: items">
<li data-bind="text: $data"></li>
</ul>
<div data-bind="visible: isVisible">Контент</div>
Computed создаются на основе одного или нескольких observable и автоматически обновляются при изменении зависимостей:
var fullName = ko.computed(function() {
return firstName() + ' ' + lastName();
});
Вычисляемые значения могут быть как read-only, так и с поддержкой записи.
Knockout поддерживает создание кастомных привязок, которые расширяют возможности фреймворка:
ko.bindingHandlers.fadeVisible = {
init: function(element, valueAccessor) {
var value = valueAccessor();
$(element).toggle(ko.unwrap(value));
},
update: function(element, valueAccessor) {
var value = valueAccessor();
ko.unwrap(value) ? $(element).fadeIn() : $(element).fadeOut();
}
};
Привязки можно применять так:
<div data-bind="fadeVisible: isVisible">Анимированный блок</div>
ViewModel описывает состояние и поведение интерфейса:
function AppViewModel() {
this.firstName = ko.observable('Иван');
this.lastName = ko.observable('Иванов');
this.items = ko.observableArray(['яблоко', 'банан']);
this.fullName = ko.computed(() => this.firstName() + ' ' + this.lastName());
}
ko.applyBindings(new AppViewModel());
Привязки автоматически обновляют DOM при изменении значений observable, а изменения в интерфейсе могут влиять на модель.
Knockout не накладывает ограничений на источник данных. Для загрузки данных с сервера часто используют fetch или библиотеки типа jQuery:
function AppViewModel() {
var self = this;
self.items = ko.observableArray([]);
fetch('/api/items')
.then(response => response.json())
.then(data => self.items(data));
}
ko.applyBindings(new AppViewModel());
При изменении items интерфейс автоматически обновляется
благодаря observableArray.
Knockout.js легко интегрируется с jQuery, Bootstrap и другими библиотеками UI. Кастомные привязки позволяют связывать сторонние виджеты с observable:
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor) {
$(element).datepicker({
onSelect: function(dateText) {
valueAccessor()(dateText);
}
});
},
update: function(element, valueAccessor) {
$(element).datepicker('setDate', ko.unwrap(valueAccessor()));
}
};
ObservableArray поддерживает методы
sort, replace, removeAll, что
упрощает управление сложными списками:
items.sort();
items.replace('яблоко', 'апельсин');
items.removeAll(['банан', 'груша']);
Привязка событий позволяет управлять действиями пользователя без прямой работы с DOM:
<button data-bind="click: addItem">Добавить</button>
self.addItem = function() {
self.items.push('новый элемент');
};
Knockout поддерживает шаблоны, которые позволяют создавать сложные иерархии элементов:
<script type="text/html" id="item-template">
<li>
<span data-bind="text: name"></span>
<button data-bind="click: $parent.removeItem">Удалить</button>
</li>
</script>
<ul data-bind="template: { name: 'item-template', foreach: items }"></ul>
Это упрощает поддержку сложных интерфейсов с вложенными компонентами.
Knockout.js обеспечивает чистое разделение данных и представления, позволяя создавать масштабируемые приложения без постоянного вмешательства в DOM. Оптимальное использование observable, computed, custom bindings и шаблонов делает интерфейсы реактивными и поддерживаемыми, а интеграция с внешними библиотеками расширяет возможности фреймворка.