Предоставляет ли KnockoutJS подходящую архитектуру для создания больших веб-приложений?
Быстрый вопрос:
Будет ли KnockoutJS обеспечивать прочную основу для разработки веб-приложения большого? Я боюсь, что одна огромная модель обзора станет недостижимой.
Фоновая информация
Я создам веб-приложение, которое будет основано на клиентской стороне. Бэкэнд будет просто конечной точкой RESTful. Весь интерфейс веб-приложения будет построен в чистом HTML/CSS/JS - никаких сценариев на стороне сервера.
Веб-приложение будет состоять из нескольких небольших приложений с одним общим именем входа (вроде веб-приложений Google, где у вас есть Gmail, Документы, Календарь, Reader и т.д.).
Каждое из этих веб-приложений будет иметь некоторые общие функции (например, представление дерева боковой панели, представление верхнего меню панели, система уведомлений) и некоторые уникальные функции приложения. Обычно я нарушаю свои приложения, чтобы инкапсулировать функциональность, например:
var myNamespace = {
common: {
settings: {},
user: {},
notifications: {}
},
app1: {},
app2: {},
app3: {}
};
Теперь мне очень нравится работать с KnockoutJS и полагаю, что это будет полезно при создании некоторых элементов моего проекта (например, системы уведомлений или расширенного представления сетки с автоматическим обновлением, поскольку приложение будет поддерживать сотрудничество). Но я просто не могу понять, куда поместить мой viewModel в эту структуру.
Я могу найти только тривиальные примеры того, как создавать приложения с помощью KnockoutJS. Можете ли вы создать что-то более продвинутое, чем читатель Twitter? Есть ли хорошие примеры того, как разбить множество функций в viewModel или, возможно, на многие viewModels?
Предлагаемое решение
В то время как более теоретический вопрос (Быстрый вопрос) по-прежнему остается без ответа, я думаю, что нашел решение, которое работает на практике. Ответ @Simon дал мне некоторую пищу для размышлений, и вот что у меня до сих пор:
// First: a collection of Observables that I want to share
ld.collectionOfObservables = {
notifications: ko.observableArray([]),
};
// Now let define a viewModel. I put all my stuff inside the
// 'ld' namespace to avoid cluttering the global object.
ld.viewModel1 = function (args) {
// Look inside args and bind all given parameters
// Normally you will want args to be an object of Observables.
for (var key in args) {
if (args.hasOwnProperty(key)) {
this[key] = args[key];
}
};
// So, by now we already have some observables in
// 'this', if there were any supplied in 'args'.
// Additionally, we define some model-unique properties/observables
this.folders = [ 'Inbox', 'Archive', 'Sent', 'Spam' ];
this.selectedFolder = ko.observable('Inbox');
};
// *** Let pretend I create similar class and call it ld.viewModel2 ***
ld.viewModel2 = function (args) { .... }
// OK, now go on and instantiate our viewModels!
// This is the fun part: we can provide 0-many observables here, by providing them in an object
// This way we can share observables among viewModels by simply suppling the same observables to different viewModels
var vm1 = new ld.viewModel1({
notifications: ld.collectionOfObservables.notifications, // we take an Observable that was defined in the collection
});
var vm2 = new ld.viewModel2({
notifications: ld.collectionOfObservables.notifications, // shared with vm1
});
// Of course, we could just send the entire ld.collectionOfObservables as an array
// but I wanted to show that you can be more flexible and chose what to share.
// Not easy to illustrate with *one* shared Observable - notifications -
// but I hope you get the point. :)
// Finally, initiate the new viewModels in a specified scope
ko.applyBindings(vm1, document.getElementById('leftPane'));
ko.applyBindings(vm2, document.getElementById('bottomPane'));
Теперь, если бы у JS было реальное наследование, это было бы еще лучше, потому что сейчас я чувствую, что все мои модели viewModels начинаются с этого:
for (var key in args) {
if (args.hasOwnProperty(key)) {
this[key] = args[key];
}
};
Но это лишь незначительное неудобство. Дайте мне знать, что вы думаете!
Изменить 1:
Может ли решение быть таким же простым, как использование привязки with:
? См. " 1. Связывание потоков управления" для примера.
Изменить 2:
Я думаю, что мое последнее редактирование было слишком быстрым. with:
привязка может помочь в структуре вашего кода, но AFAIK это не поможет вам обмениваться наблюдаемыми между этими разными частями. Таким образом, предлагаемое решение выше по-прежнему остается.
Ответы
Ответ 1
Вы можете использовать частичные представления и обмениваться наблюдаемыми между ними.
var some_observable = ko.observable()
var Model1 = function(something) {
this.something_1 = something;
};
var Model2 = function(something) {
this.something_2 = something;
};
var view_1 = Model1(some_observable);
var view_2 = Model2(some_observable);
ko.applyBindings(view_1, document.getElementById('some-id'));
ko.applyBindings(view_2, document.getElementById('some-other-id'));
<div id='some-id'>
<input data-bind='value: something_1' />
</div>
<div id='some-other-id'>
<input data-bind='value: something_2' />
</div>
Я использую этот aproach для ведения списка фотографий в приложении галереи, где один вид создает эскизы, а другой вид заботится о загрузках.
Ответ 2
Я использовал частичные представления (в Нэнси, а не MVC), каждый из которых имеет свою собственную задачу нокаута, каждая из которых имеет свою собственную модель представления. Я думаю, что он работает красиво - сложная страница разбита на многие простые независимые частичные. Большинство частичных представлений имеют свой собственный модуль/контроллер/конечную точку, поэтому модули тоже "тощие".
Это позор по поводу отбрасывания шаблонов jQuery, но это другая проблема.
Извините, я просто перечитываю ваше сообщение: никаких побочных действий на стороне сервера, так что нет возможности разбить страницу? Уч. Я все еще думаю, что многие модели просмотра - это путь.
Ответ 3
На мой взгляд, мы могли бы использовать KO и делиться View Models с объемом функционального модуля (например, функциональный виджет с несколькими элементами управления html). Мы могли бы изучить TIBCO Page bus (Pub/Sub) для обмена данными между этими функциональными модулями на странице, чтобы поддерживать функциональные модули в развязанной странице и управляемым образом.
Ответ 4
Это старый пост, но недавно я создал фреймворк для той же цели в этот репозиторий, который я называю gcc-нокаутом. Все является компонентом, и даже менеджер представлений может полностью переключать представления и сохранять историю в одно время. Я еще не задокументировал это еще раз, но в репо есть пример, демонстрирующий некоторые его функции.
Обратите внимание, что я также использовал компилятор Google Closure. Вы можете безопасно использовать его в расширенном режиме, учитывая, что вы правильно экспортируете свойства, которые будете использовать в html-шаблонах. Компоненты общаются с помощью goog.events, и все довольно чисто прямо сейчас. Я не использовал утилиту subscribe для подписчиков. Не стесняйтесь проверять и вносить свой вклад! Я иногда обновляю его.