Knockout.js основан на концепции Model-View-ViewModel (MVVM), где ключевой задачей является четкое разделение логики приложения и пользовательского интерфейса. Эффективное разделение логики между компонентами повышает поддерживаемость, облегчает тестирование и упрощает масштабирование приложений.
Компонент в Knockout.js представляет собой комбинацию ViewModel и связанной с ним разметки. Типичная структура компонента:
// ViewModel компонента
function ProductViewModel(params) {
this.name = ko.observable(params.name || "");
this.price = ko.observable(params.price || 0);
this.quantity = ko.observable(params.quantity || 1);
this.total = ko.computed(() => this.price() * this.quantity());
}
Разметка компонента связывается с ViewModel через
data-bind:
<div data-bind="with: product">
<span data-bind="text: name"></span>
<span data-bind="text: price"></span>
<input type="number" data-bind="value: quantity" />
<span data-bind="text: total"></span>
</div>
Регистрация компонента производится с помощью
ko.components.register:
ko.components.register('product-item', {
viewModel: ProductViewModel,
template: '<div data-bind="with: $data">' +
'<span data-bind="text: name"></span>' +
'<span data-bind="text: price"></span>' +
'<input type="number" data-bind="value: quantity" />' +
'<span data-bind="text: total"></span>' +
'</div>'
});
Каждый компонент должен инкапсулировать только свою функциональность. Важные принципы:
params — все
необходимые данные передаются компоненту через параметры при его
создании.observable и
computed — локальные состояния и вычисления должны
оставаться внутри ViewModel компонента.Пример передачи данных в компонент:
<product-item params="name: 'Laptop', price: 1200"></product-item>
ko.applyBindings({});
Компоненты часто требуют взаимодействия между собой. Knockout.js рекомендует следующие подходы:
params) — передача
функций обратного вызова или observables, позволяющих обмениваться
данными.function CartViewModel() {
this.items = ko.observableArray([]);
this.addItem = function(item) {
this.items.push(item);
};
}
<product-item params="name: 'Phone', price: 600, onAdd: addItem"></product-item>
Knockout.js позволяет делить ViewModel на миксины или функции, которые можно использовать в нескольких компонентах:
function PriceMixin(basePrice) {
return {
price: ko.observable(basePrice),
discount: ko.observable(0),
finalPrice: ko.computed(function() {
return this.price() - this.discount();
}, this)
};
}
function ProductViewModel(params) {
Object.assign(this, PriceMixin(params.price));
this.name = ko.observable(params.name || "");
}
Такой подход позволяет избежать дублирования кода и легко поддерживать вычисляемые свойства.
Для разделения логики событий применяется привязка через
click, valueUpdate и другие
биндинги:
<button data-bind="click: addToCart">Добавить в корзину</button>
this.addToCart = function() {
if (typeof params.on Add === 'function') {
params.onAdd({ name: this.name(), price: this.price() });
}
};
Это позволяет компоненту оставаться самодостаточным, передавая взаимодействие с внешним миром через параметры.
При масштабировании приложения:
<cart>
<product-item params="name: 'Laptop', price: 1200"></product-item>
<product-item params="name: 'Mouse', price: 40"></product-item>
</cart>
Такой подход обеспечивает чистую архитектуру, облегчает отладку и последующее расширение функциональности.