У меня есть довольно большое одностраничное приложение Knockout.js. (в настоящее время более 20 тысяч строк кода), который очень легко поддерживать и добавлять в него дополнительные разделы. У меня есть сотни наблюдаемых, и производительность по-прежнему отличная, даже на мобильных устройствах, таких как старый iPod touch. По сути, это приложение, в котором размещен набор инструментов. Вот некоторые сведения о приложении, которое я использую:
1. Только одна модель представления. Это делает вещи простыми ИМХО.
Модель представления обрабатывает основы любого одностраничного приложения, такие как видимость каждой страницы (приложения), навигация, ошибки, загрузка и всплывающие диалоговые окна и т. д. Пример фрагмента модели представления: (я выделяю еще больше файлов js, но это для того, чтобы дать вам представление о том, как это выглядит)
var vm = {
error:
{
handle: function (error, status)
{
//Handle error for user here
}
},
visibility:
{
set: function (page)
{
//sets visibility for given page
}
},
permissions:
{
permission1: ko.observable(false),
permission2: ko.observable(false)
//if you had page specific permissions, you may consider this global permissions and have a separate permissions section under each app
},
loadDialog:
{
message: ko.observable(''),
show: function (message)
{
//shows a loading dialog to user (set when page starts loading)
},
hide: function()
{
//hides the loading dialog from user (set when page finished loading)
}
},
app1:
{
visible: ko.observable(false),
load: function ()
{
//load html content, set visibility, app specific stuff here
}
},
app2:
{
visible: ko.observable(false),
load: function ()
{
//load html content, set visibility, app specific stuff here
}
}
}
2. Все модели помещаются в отдельные файлы .js.
Я рассматриваю модели как классы, поэтому все, что они на самом деле делают, — это хранят переменные и имеют несколько основных функций форматирования (я стараюсь, чтобы они были простыми). Пример модели:
//Message Class
function Message {
var self = this;
self.id = ko.observable(data.id);
self.subject = ko.observable(data.subject);
self.body = ko.observable(data.body);
self.from = ko.observable(data.from);
}
3. Держите вызовы базы данных AJAX в своих собственных файлах js.
Желательно разделить по разделам или «приложениям». Например, ваше дерево папок может быть js/database/ с app1.js и app2.js в виде js-файлов, содержащих ваши основные функции создания, извлечения, обновления и удаления. Пример вызова базы данных:
vm.getMessagesByUserId = function ()
{
$.ajax({
type: "POST",
url: vm.serviceUrl + "GetMessagesByUserId", //Just a default WCF url
data: {}, //userId is stored on server side, no need to pass in one as that could open up a security vulnerability
contentType: "application/json; charset=utf-8",
dataType: "json",
cache: false,
success: function (data, success, xhr)
{
vm.messaging.sent.messagesLoaded(true);
for (var i = 0; i < data.messages.length; i++)
{
var message = new Message({
id: data.messages[i].id,
subject: data.messages[i].subject,
from: data.messages[i].from,
body: data.messages[i].body
});
vm.messaging.sent.messages.push(message);
}
},
error: function (jqXHR)
{
vm.error.handle(jqXHR.getResponseHeader("error"), jqXHR.status);
}
});
return true;
};
4. Объедините и уменьшите все js-файлы вашей модели, модели представления и базы данных в один.
Я использую расширение Visual Studio «Web Essentials», которое позволяет создавать «связанные» js-файлы. (Выберите файлы js, щелкните их правой кнопкой мыши и перейдите в Web Essentials -> Создать файл пакета Javascript). Мой файл пакета настроен следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<bundle minify="true" runOnBuild="true">
<!--The order of the <file> elements determines the order of them when bundled.-->
<!-- Begin JS Bundling-->
<file>js/header.js</file>
<!-- Models -->
<!-- App1 -->
<file>js/models/app1/class1.js</file>
<file>js/models/app1/class2.js</file>
<!-- App2 -->
<file>js/models/app2/class1.js</file>
<file>js/models/app2/class2.js</file>
<!-- View Models -->
<file>js/viewModel.js</file>
<!-- Database -->
<file>js/database/app1.js</file>
<file>js/database/app2.js</file>
<!-- End JS Bundling -->
<file>js/footer.js</file>
</bundle>
Заголовки header.js и footer.js — это всего лишь оболочка для функции готовности документа:
header.js:
//put all views and view models in this
$(document).ready(function()
{
footer.js:
//ends the jquery on document ready function
});
5. Разделите свой HTML-контент.
Не храните один большой чудовищный HTML-файл, в котором трудно ориентироваться. Вы можете легко попасть в эту ловушку с нокаутом из-за привязки нокаута и безгражданства протокола HTTP. Тем не менее, я использую два варианта разделения в зависимости от того, рассматриваю ли я фрагмент как доступный многим пользователям или нет:
Серверная сторона включает: (просто указатель на другой html-файл. Я использую его, если чувствую, что эта часть приложения часто используется пользователями, но я хочу сохранить ее отдельно)
<!-- Begin Messaging -->
<!--#include virtual="Content/messaging.html" -->
<!-- End Messaging -->
Вы не хотите использовать серверную часть слишком много, иначе объем HTML, который пользователь должен будет загружать каждый раз, когда они посещают страницу, станет довольно большим. С учетом сказанного, это, безусловно, самое простое решение для разделения вашего html, но при этом сохранить привязку нокаута на месте.
Асинхронная загрузка содержимого HTML: (я использую это, если данная часть приложения используется пользователями реже)
Для этого я использую функцию загрузки jQuery:
// #messaging is a div that wraps all the html of the messaging section of the app
$('#messaging').load('Content/messaging.html', function ()
{
ko.applyBindings(vm, $(this)[0]); //grabs any ko bindings from that html page and applies it to our current view model
});
6. Следите за видимостью ваших страниц/приложений
Отображение и скрытие различных разделов вашего приложения Knockout.js может легко сойти с ума из-за множества строк кода, которыми трудно управлять и запоминать, потому что вам приходится устанавливать так много разных переключателей включения и выключения. Во-первых, я держу каждую страницу или приложение в своем собственном «div» (и в своем собственном html-файле для разделения). Пример HTML:
<!-- Begin App 1 -->
<div data-bind="visible: app1.visible()">
<!-- Main app functionality here (perhaps splash screen, load, or whatever -->
</div>
<div data-bind="visible: app1.section1.visible()">
<!-- A branch off of app1 -->
</div>
<div data-bind="visible: app1.section2.visible()">
<!-- Another branch off of app1 -->
</div>
<!-- End App 1 -->
<!-- Begin App 2 -->
<div data-bind="visible: app2.visible()">
<!-- Main app functionality here (perhaps splash screen, load, or whatever -->
</div>
<!-- End App 2 -->
Во-вторых, у меня была бы функция видимости, подобная этой, которая устанавливает видимость для всего контента на вашем сайте: (она также обрабатывает мою навигацию в подфункции)
vm.visibility:
{
set: function (page)
{
vm.app1.visible(page === "app1");
vm.app1.section1.visible(page === "app1section1");
vm.app1.section2.visible(page === "app1section2");
vm.app2.visible(page === "app2");
}
};
Затем просто вызовите функцию загрузки приложения или страницы:
<button data-bind="click: app1.load">Load App 1</button>
В котором будет эта функция:
vm.visibility.set("app1");
Это должно охватывать основы большого одностраничного приложения. Вероятно, есть лучшие решения, чем то, что я представил, но это неплохой способ сделать это. Несколько разработчиков могут легко работать над разными разделами приложения без конфликтов с контролем версий и так далее.
person
Gaff
schedule
12.03.2013