ViewModel в Knockout.js представляет собой объект, который инкапсулирует данные и поведение пользовательского интерфейса. Он служит посредником между моделью данных и представлением, обеспечивая двустороннюю привязку (two-way data binding). В отличие от традиционного подхода, где состояние интерфейса хранится напрямую в DOM, ViewModel позволяет управлять состоянием через JavaScript, что упрощает тестирование и поддержку кода.
Создание простого ViewModel выглядит следующим образом:
function AppViewModel() {
this.firstName = ko.observable("Иван");
this.lastName = ko.observable("Иванов");
this.fullName = ko.computed(() => {
return this.firstName() + " " + this.lastName();
});
}
ko.applyBindings(new AppViewModel());
observable — специальный объект Knockout.js, который
отслеживает изменения значения и автоматически обновляет привязанные
элементы интерфейса.computed — вычисляемое свойство, которое автоматически
пересчитывается при изменении зависимых observables.Для крупных приложений рекомендуется использовать модульную структуру ViewModel, разделяя логику на отдельные компоненты. Такой подход позволяет изолировать функциональность и облегчает тестирование.
function AddressViewModel(street, city) {
this.street = ko.observable(street);
this.city = ko.observable(city);
}
function PersonViewModel(firstName, lastName, address) {
this.firstName = ko.observable(firstName);
this.lastName = ko.observable(lastName);
this.address = address;
this.fullName = ko.computed(() => {
return this.firstName() + " " + this.lastName();
});
}
const address = new AddressViewModel("Ленина", "Москва");
const person = new PersonViewModel("Иван", "Иванов", address);
ko.applyBindings(person);
Ключевые моменты:
observableArray, что удобно для списков и таблиц:function AppViewModel() {
this.people = ko.observableArray([
new PersonViewModel("Иван", "Иванов", new AddressViewModel("Ленина", "Москва")),
new PersonViewModel("Мария", "Петрова", new AddressViewModel("Садовая", "Санкт-Петербург"))
]);
this.addPerson = () => {
this.people.push(new PersonViewModel("Новый", "Пользователь", new AddressViewModel("", "")));
};
}
Knockout.js позволяет реагировать на изменения observables с помощью подписок. Это полезно для выполнения побочных эффектов при изменении данных:
this.firstName.subscribe((newValue) => {
console.log("Имя изменилось на:", newValue);
});
Подписки могут применяться как к одиночным observables, так и к computed свойствам.
ViewModel часто взаимодействует с внешними источниками данных через
AJAX или fetch. Для интеграции асинхронных данных используется
комбинация observable и промисов:
function AppViewModel() {
this.users = ko.observableArray([]);
fetch("https://api.example.com/users")
.then(response => response.json())
.then(data => {
const userModels = data.map(user => new PersonViewModel(user.firstName, user.lastName, new AddressViewModel(user.street, user.city)));
this.users(userModels);
});
}
Преимущества такого подхода:
Knockout.js поддерживает компонентный подход. Компоненты позволяют инкапсулировать шаблон и ViewModel:
ko.components.register('person-card', {
viewModel: function(params) {
this.person = params.person;
},
template: `
<div>
<h2 data-bind="text: person.fullName"></h2>
<p data-bind="text: person.address.street + ', ' + person.address.city"></p>
</div>
`
});
Использование компонента в основном шаблоне:
<div data-bind="foreach: people">
<person-card params="person: $data"></person-card>
</div>
Преимущества компонентов:
Эффективный подход к построению ViewModel включает:
observable и computed для
отслеживания состояния.observableArray для динамических
списков.Такой подход обеспечивает поддерживаемость, тестируемость и масштабируемость приложения на Knockout.js.