Knockout.js — это JavaScript-фреймворк для реализации паттерна MVVM (Model-View-ViewModel), где ViewModel управляет состоянием пользовательского интерфейса и связывает его с моделью данных. Управление глобальным состоянием является важным аспектом крупных приложений, позволяя централизованно хранить данные, которые нужны разным компонентам интерфейса, и обеспечивать их синхронизацию.
В Knockout.js глобальное состояние обычно представлено наблюдаемыми объектами (observables) и наблюдаемыми массивами (observableArray). Эти структуры автоматически уведомляют подписчиков о любых изменениях, что позволяет обновлять интерфейс без ручного вмешательства.
var appState = {
user: ko.observable({ name: 'Иван', age: 30 }),
notifications: ko.observableArray([]),
isLoggedIn: ko.observable(false)
};
Любой элемент интерфейса, привязанный к глобальному состоянию, автоматически обновляется при изменении данных:
<div data-bind="visible: appState.isLoggedIn">
Привет, <span data-bind="text: appState.user().name"></span>!
</div>
// Изменение имени пользователя
appState.user({ name: 'Алексей', age: 30 });
После вызова appState.user(...) элемент
<span> обновит текст автоматически.
Для больших приложений рекомендуется использовать один главный ViewModel, который агрегирует отдельные модули состояния:
function UserModule() {
this.name = ko.observable('Иван');
this.age = ko.observable(30);
}
function NotificationModule() {
this.notifications = ko.observableArray([]);
}
function AppViewModel() {
this.userModule = new UserModule();
this.notificationModule = new NotificationModule();
this.isLoggedIn = ko.observable(false);
}
var appViewModel = new AppViewModel();
ko.applyBindings(appViewModel);
Такой подход позволяет:
Knockout.js позволяет создавать вычисляемые наблюдаемые значения (computed observables), которые автоматически обновляются при изменении зависимостей:
appViewModel.greeting = ko.computed(function() {
return appViewModel.isLoggedIn() ?
`Привет, ${appViewModel.userModule.name()}!` :
'Пожалуйста, войдите в систему';
});
Любые элементы, привязанные к greeting, обновятся при
изменении isLoggedIn или userModule.name.
Подписки (subscribe) позволяют выполнять произвольный
код при изменении состояния:
appViewModel.isLoggedIn.subscribe(function(newValue) {
if (newValue) {
console.log('Пользователь вошел в систему');
} else {
console.log('Пользователь вышел');
}
});
Для крупных проектов удобно выносить глобальное состояние в отдельный модуль:
// state.js
export const state = {
user: ko.observable({ name: '', age: 0 }),
isLoggedIn: ko.observable(false),
notifications: ko.observableArray([])
};
Подключение модуля в компоненты:
import { state } from './state.js';
function HeaderViewModel() {
this.userName = ko.computed(() => state.user().name);
this.logout = () => state.isLoggedIn(false);
}
Такой подход позволяет разным частям приложения использовать одни и те же данные без дублирования.
Глобальное состояние часто синхронизируется с сервером через
AJAX-запросы. Knockout.js не предоставляет встроенные средства работы с
сетью, но удобно использовать fetch или сторонние
библиотеки:
function loadUser() {
fetch('/api/user')
.then(response => response.json())
.then(data => state.user(data))
.catch(err => console.error(err));
}
Любые изменения в state.user автоматически обновляют
интерфейс, если элементы связаны через привязки.
function TaskModule() {
this.tasks = ko.observableArray([]);
this.completedTasks = ko.computed(() => this.tasks().filter(t => t.done));
this.addTask = task => this.tasks.push({ name: task, done: false });
this.completeTask = taskName => {
const task = this.tasks().find(t => t.name === taskName);
if (task) task.done = true;
this.tasks.valueHasMutated();
};
}
function AppViewModel() {
this.userModule = new UserModule();
this.notificationModule = new NotificationModule();
this.taskModule = new TaskModule();
this.isLoggedIn = ko.observable(false);
}
const appViewModel = new AppViewModel();
ko.applyBindings(appViewModel);
В этом примере глобальное состояние объединяет данные пользователя, уведомления и задачи, при этом каждый модуль имеет собственные методы управления, что обеспечивает удобство поддержки и расширяемость.