1. Предисловие
В предыдущей теме я быстро представил знания, связанные с нокаутированием, и написал несколько простых примеров. Я надеюсь, что с помощью этих примеров вы можете быстро начать с нокаутирований. Чтобы все ясно просматривали применение nockoutjs в реальных проектах, эта тема представит, как использовать Webapi+Bootstrap+nockoutjs+ASP.Net MVC для создания веб-программы на одну страницу. Эта модель сейчас также используется в реальных проектах большинства компаний.
2. Spa (одиночная страница) преимущества
Прежде чем представить конкретную реализацию, я чувствую, что необходимо подробно ввести SPA. SPA, аббревиатура веб -приложения для одной страницы, представляет собой веб -приложение, которое загружает одну страницу HTML и динамически обновляет страницу, когда пользователь взаимодействует с приложением. Браузер загрузит необходимые HTML, CSS и JavaScript в начале. Все операции завершены на этой странице и контролируются JavaScript.
Преимущества одностраничных программ:
Лучший пользовательский опыт позволяет пользователям испытывать скорость и плавность нативных приложений в веб -приложении.
Разделите проблемы с передним и задним концом, передняя часть отвечает за дисплей интерфейса, а задний конец отвечает за хранение и вычисления данных, каждый выполняет свои собственные обязанности и не будет смешивать логику передней и задней части.
Чтобы уменьшить давление на сервер, сервер должен только генерировать данные, независимо от логики отображения и логики страниц, а также увеличить пропускную способность сервера. Передний конец, написанный в синтаксисе бритвы в MVC, требует, чтобы сервер завершил синтез страницы, а затем выводил его.
Один и тот же набор программ движения может использоваться непосредственно на нескольких клиентах, таких как веб-интерфейс, мобильные телефоны, планшеты и т. Д. Без модификации.
Конечно, в дополнение к преимуществам, перечисленным выше, одностраничные программы также имеют свои недостатки:
Не способствует SEO. Если это система управления, это не повлияет на нее.
Первоначальное время загрузки относительно увеличено. Потому что все ресурсы JS и CSS будут загружены в первый раз, что делает последующую страницу плавной. Для этого вы можете использовать пакет в ASP.NET MVC для привязки файлов. Для получения подробного использования пакета, пожалуйста, см. Статьи: http://www.vevb.com/article/84329.htm, http://www.vevb.com/article/84329.htm и http://www.vevb.com/article/82174.htm.
Навигация недоступна. Если вам нужно ориентироваться, вы должны продвинуться и отступить самостоятельно. Для этого вы можете самостоятельно реализовать функции вперед и назад, чтобы восполнить это. На самом деле, это то, что делают мобильные веб -страницы сейчас, и теперь им все еще нужно быть навигацией выше. Это также можно сделать для некоторых систем управления бэкэнд предприятия.
Высокие затраты на разработку для разработчиков. Это не проблема. Программисты должны продолжать учиться заряжать. К счастью, некоторые фронтальные рамки очень просты в использовании.
3. Используйте ASP.NET MVC+WebAPI+Bootstrap+nockoutjs для реализации SPA
Преимущества и недостатки спа -салона были введены подробно ранее. Затем давайте используем ASP.NET MVC+WebAPI+BS+KO для реализации программы на одну страницу, чтобы испытать плавность SPA и сравнить эффекты страниц, выполненных оригинальной ASP.NET MVC+Razor.
1. Используйте VS2013, чтобы создать проект веб -приложения ASP.NET, проверьте библиотеку MVC и WebAPI. См. Рисунок ниже для получения подробной информации:
2. Создать соответствующие склады и модели. Вот простая система управления задачами. Конкретная модель и код склада следующие:
Внедрение класса объекта задачи:
public enum taskstate {active = 1, завершено = 2} /// <summary> /// asse entity //// </summary> public class task {public int id {get; набор; } public String name {get; набор; } public String Описание {get; набор; } public DateTime CreationTime {get; набор; } public DateTime FinishTime {get; набор; } public String владелец {get; набор; } public Taskstate State {get; набор; } public task () {creationTime = dateTime.parse (datetime.now.tolongdatestring ()); State = taskstate.active; }}Внедрение класса склада задач:
/// <summary> /// Здесь склад использует образцы данных в качестве демонстрации. Реальный проект должен быть динамически загружать из базы данных /// </summary> public class taskrepository {#region static, поданный частным статическим Lazy <saseRepository> _taskRepository = new Lazy <saseRepository> (() => New TaskRepository ()); public Static TaskRepository Current {get {return _taskRepository.value; }}. Datetime.parse (datetime.now.adddays (1) .toString (cultureInfo.invariantculture))}, новая задача {id = 2, name = "Learning nockoutjs", description = "nockoutjs-это библиотека класса MVVM, которая поддерживает двухстороннее привязку", владелец = "tommy li, finelmime = funithimtime = DateTime.parse (datetime.now.adddays (2) .toString (cultureInfo.invariantculture))}, новая задача {id = 3, name = "Learn AngularJs", описание = "AngularJs - это структура MVVM, которая интегрирует MVVM и MVC с одним. Datetime.parse (datetime.now.adddays (3) .toString (cultureInfo.invariantculture))}, новая задача {id = 4, name = "Learn Asp.net Mvc Веб -сайт", Description = "Mivsse - это инструмент тестирования производительности под .NET, который поддерживает ASP.NET, asp.net MVC, EF, и Effect, и это не то, что это не так, что это не так, что это не так. может вывести время выполнения каждой ссылки на выполнение кода ", akens =" tonny li ", finishtime = dateTime.parse (dateTime.now.adddays (4) .toString (cultureInfo.invariantculture))},}; #endregion #region public Methods public ienumerable <sase> getall () {return _tasks; } публичная задача get (int id) {return _tasks.find (p => p.id == id); } публичная задача добавить (элемент задачи) {if (item == null) {бросить новый аргумент. } item.id = _tasks.count + 1; _tasks.add (item); вернуть элемент; } public void remove (int id) {_tasks.removeall (p => p.id == id); } public bool update (элемент задачи) {if (item == null) {бросить новый аргумент. } var taskitem = get (item.id); if (taskitem == null) {return false; } _tasks.remove (taskitem); _tasks.add (item); вернуть истину; } #endregion}3. Добавить библиотеки Bootstrap и nockoutjs через Nuget.
4. Внедрить сервисные службы данных. Здесь бэкэнд -сервис реализована с использованием asp.net webapi. Конкретный код реализации выглядит следующим образом:
/// <summary> /// Задача webAPI, предоставление данных данных /// </summary> открытый класс taskscontroller: apicontroller {private readonly taskRepository _taskRepository = taskRepository.current; public ienumerable <sase> getall () {return _taskRepository.getall (). orderby (a => a.id); } публичная задача get (int id) {var item = _taskRepository.get (id); if (item == null) {бросить новое httpresponseexception (httpstatuscode.notfound); } return Item; } [Route ("api/tasks/getbystate")] public ienumerable <sasse> getBystate (String State) {ienumerable <sase> results = new List <sase> (); switch (state.tolower ()) {case "": case "All": results = _taskRepository.getall (); перерыв; case "Active": results = _taskRepository.getall (). где (t => t.state == taskstate.active); перерыв; case "Завершен": Results = _taskRepository.getall (). где (t => t.state == taskstate.completed); перерыв; } Results = Results.orderby (t => t.id); вернуть результаты; } [Httppost] публичная задача Create (задача) {return _taskrepository.add (item); } [Httpput] public void put (atem) {if (! _TaskRepository.update (item)) {бросить новое httpresponseexception (httpstatuscode.notfound); }} public void delete (int id) {_taskRepository.remove (id); }}5. Используйте пакет ASP.NET MVC, чтобы упаковать ресурсы. Соответствующий код реализации Bundleconfig заключается в следующем:
/// <summary> /// Просто добавьте несколько отсутствующих файлов CSS и JS. Поскольку некоторые файлы CSS и JS были добавлены при создании шаблона /// </summary> Public Class Bundleconfig {// Для получения дополнительной информации о связке, посетите http://go.microsoft.com/fwlink/?linkid=301862 public static void registerbundles (bundlecollection) ScriptBundle ("~/bundles/jQuery"). Include ("~/scripts/jQuery- {version} .js")); bundles.add (new ScriptBundle ("~/bundles/jQueryval"). include ("~/scripts/jQuery.validate*")); // Использование версии разработки Modernizr для развития и учиться. Затем, когда вы // готовы к производству, используйте инструмент сборки по адресу http://modernizr.com, чтобы выбрать только необходимые вам тесты. bundles.add (новый ScriptBundle ("~/bundles/modernizr"). include ("~/scripts/modernizr-*")); bundles.add (новый ScriptBundle ("~/bundles/bootstrap"). include ("~/scripts/bootstrap.js", "~/scripts/bootstrap-datepicker.min.js")); bundles.add (new Stylebundle ("~/content/css"). include ("~/content/bootstrap.css", "~/content/bootstrap datepicker3.min.css", "~/content/site.css")); bundles.add (new ScriptBundle ("~/bundles/nockout"). include ("~/scripts/nockout- {version} .js", "~/scripts/nockout.validation.min.js", "~/scripts/nockout.mapping-latest.js")); bundles.add (new ScriptBundle ("~/bundles/app"). include ("~/scripts/app/app.js")); }}6. Потому что нам нужно сделать тип перечисления появляться как строка на странице. Перечисление преобразуется в числовой тип при сериализированном по умолчанию. Поэтому нам нужно внести следующие изменения в классе WebApiconfig:
Public Static Class webApiconfig {public Static void Register (конфигурация httpconfiguration) {// configuration and services / services // web API config.maphttpattributeRoutes (); config.routes.maphttproute (name: "defaultapi", routetemplate: "api/{controller}/{id}", по умолчанию: new {id = routeParameter.optional}); // Сделать сериализацию Использовать CAMEL CASE SELICE SERIALIZATION Свойство config.formatters.jsonformatter.serializersettings.contractresolver = new camelcasepropertynamescontractresolver (); // serialize string config.formatters.jsonformatter.serializersettings.converters.add (new StringenumConverter ()); }}ПРИМЕЧАНИЕ. Если сериализация нижней части верблюда не используется выше, вы также должны вносить коррективы при привязке данных на странице. Например, при привязке атрибута имени напрямую используйте капитализацию имени. Если вы используете метод имени, он подскажет, что в этом атрибуте нет ошибки определения. Поскольку JS использует стиль стропительного управления верблюда, чтобы назвать переменные. Следовательно, рекомендуется использовать стиль строчной строки верблюда для сериализации. В настоящее время вы можете использовать только форму «имени» для привязки. Это больше соответствует спецификациям кода JS.
7. Измените соответствующий файл макета и содержимое файла индекса.
Конкретный код файла макета заключается в следующем:
<! Doctype html> <html> <head> <meta charset = "utf-8"/> <meta name = "viewport" content = "width = width device, начальная масштаба = 1.0"> <Title> Приложение SPA LearningHard </title> @stoles.render ("~/content/csss") @scripts <body> <div> <div> <d Div> <p> Простая система управления задачами </p> </div> <div> <ul> <li> <a href = "/"> home </a> </li> </ul> </div> </div> </div> <div Id = "main"> @renderbody () <hr/> <copeer> Приложение </p> </cooler> </div> @scripts.render ("~/bundles/jquery") @scripts.render ("~/bundles/bootstrap") @scripts.render ("~/bundles/nockout") @scripts.render ("~/bundles/нокаут") @scripts.renders ("~/bundles") @scripts. </body> </html> код страницы индекса следующим образом: @{viewbag.title = "index"; Layout = "~/Views/Shared/_Layout.cshtml";}<div id="list" data-bind="if:canCreate"><h2>Tasks</h2><div> <table> <thead> <tr> <th>Number</th> <th>Name</th> <th>Description</th> <th>People in charge</th> <th>Creation time</th> <Th> время завершения </th> <Th> statU </th> <Th> </th> </tr> </t> <tbody data-bind = "foreach: tasks"> <tr> <td data-bind = "text: id"> </td> <td> <a Data-bind = "text: name: handlecReateorUpdate" data-bind = "text: description"> </td> <td data-bind = "text: владелец"> </td> <td data-bind = "text: creationtime"> </td> <td data-bind = "text: finishing"> </td> <td data-bind = "text: atation"> </td> <td> <a Data-bind = "recate:" remove: "recate" remove "> </td> <td> <a Data-bind =" remoble: "remove:" remove: "remove:" remove exected " href = "javascript: void (0)"> удалить </a> </td> </tbody> </table> </div> <div> <a href = "javascript: void (0)" data-bind = "click: function (data, event) {settasklist ('все')}"> все </a> | <a href = "javascript: void (0)" data-bind = "click: function (data, event) {settakStist ('active')}"> active </a> | <a href = "javascript: void (0)" data-bind = "click: function (data, event) {settaklist ('reflect')}"> завершен </a> </div> <div> <a href = "javascript: void (0)" data-bind = "click: handlecreateOrupDate"> divEd oD = divE = divE = divE = divE> divE = divE> divE = divE> diving xed vectibilte "</diving> diving vectiby vecle vectibilte vide vectiby od = diving vise exteor hidden "> <h2> добавить задачу </h2> <br/> <div> <div> <label for =" taskname "> name*</label> <div> <input type =" text "bind =" value: name "id =" taskName "name =" taskName "wackHoter =" name "> </div> </div> <div> <div> <div> <diver for for for taskdesc"/div> </div> <div> <div> <div> <div> <dive> <TextAREA DATA-BIND = "Значение: Описание" ROW = "3" ID = "taskDESC" name = "taskDesc" Placeholder = "description"> </textARea> </div> </div> <div> <label for = "taskowner"> Человек, ответственный*</label> <div> <input id = "taskowner" = "taskowner" data-bind = vatue ». </div> </div> <div> <label for="taskFinish">Estimated completion time*</label> <div> <input id="taskFinish" data-bind="value: finishTime" name="taskFinish"> </div> </div> <div> <label for="taskOwner">Status*</label> <div> <select id="taskState" data-bind="value: state"> <purint> Active </option> <purity> Завершен </option> </select> </div> </div> <div> <div> <кнопка data-bind = "click: handlesaveclick"> save </button> <кнопка Data-bind = "click: handbackclick"> Back </utturn> </div> </div> </div> </div>8. Создайте соответствующую логику сценария фронта. Используйте код JS, чтобы запросить данные и создать соответствующие объекты ViewModel для привязки фронта. Конкретный код реализации JS выглядит следующим образом:
var asklistviewmodel = {tasks: ko.observablearray (), cancreate: ko.observable (true)}; var taskmodel = function () {this.id = 0; this.name = ko.observable (); this.description = ko.observable (); this.finishtime = ko.observable (); this.owner = ko.observable (); this.state = ko.observable (); this.fromjs = function (data) {this.id = data.id; this.name (data.name); this.description (data.description); this.finishtime (data.finishtime); это. this.state (data.state); / });} function settaskList (state) {sendAjaxRequest ("get", function (data) {taskListViewModel.tasks.removeall (); for (var i = 0; i <data.length; i ++) {taskListViewModel.tasks.push (data]; });} функция удалить (item) {sendajaxRequest ("delete", function () {getAlltasks ();}, item.id);} var task = new TaskModel (); function handlecreateorUpdate (item) {task.fromjs (item); initDatePicker (); tasklistviewmodel.cancreate (false); $ ('#create'). CSS ('Visibility', 'visible');} функция handbackclick () {taskListViewModel.cancReate (true); $ ('#create'). CSS ('Visibility', 'hidden');} функция handlesaveClick (item) {if (item.id == undefined) {sendAjaxRequest ("post", function (newItem) {// nehiTem - это возвращаемый объект. Описание: item.description, finishtime: item.finishtime, владелец: item.owner, atation: item.state}); } else {sendAjaxRequest ("put", function () {getAlltasks ();}, null, {id: item.id, name: item.name, description: item.description, ittime: item.finishtime, владелец: item.owner, atation: item.state}); } taskListViewModel.cancReate (true); $ ('#create'). css ('viseibility', 'hidden');} функция sendajaxRequest (httpmethod, callback, url, reqdata) {$ .ajax ("/api/tasks" + (url? "/" + url: ") });} var initDatePicker = function () {$ ('#create .datePicker'). DatePicker ({autoclose: true});}; $ ('. Nav'). On ('chlick', 'li', function () {$ ('. Nav Li.active'). removClass ('Active'); $ (this) .AdClass ('active');}); $ (document) .ready (function () {getAlltasks (); // Использовать nockoutjs, чтобы связать ko.applybindings (taskListViewModel, $ ('#list'). get (0)); Ko.ApplyBindings (задача, $ (#create '). get (0);На данный момент наша одностраничная программа была разработана. Далее, давайте запустим его, чтобы увидеть его эффект.
На приведенной выше с диаграммой демонстрации результатов мы видим, что после загрузки страницы все операции, похоже, работают на одной странице, и кажется, что страница браузера кружит. По сравнению с страницами, разработанными с использованием ASP.NET MVC + Razor, вы чувствуете гладкость спа -салона? Ранее, страница, разработанная с использованием ASP.NET MVC + Razor, вам просто нужно запросить одну страницу, и вы можете почувствовать обновление всей страницы, что делает пользовательский опыт очень плохим.
4. Сравнение с моделью разработки бритвы
Я считаю, что все видели преимущества спа -салона из результатов. Далее, я думаю, что необходимо сравнить его с традиционным способом реализации веб -страниц. Есть два основных отличия от метода разработки бритвы:
1. Когда страница отображается, данные обрабатываются на стороне браузера. Не на сервере. Выделите давление рендеринга на сторону браузера каждого пользователя, тем самым снижая давление на сервер веб -сайтов. Если бы это был синтаксис бритвы, оператор связывания передней страницы должен быть следующим:
@Model ienumerable <nockoutjsspa.models.task> @foreach (var in model) {<tr> <td>@item.name </td> <td>@item.description </td> </tr>}Они отображаются в экипировке бритвы на стороне сервера. Это также причина, по которой страницы, разработанные с использованием Razor, увидят, что страницы кружат. Поскольку каждый раз, когда вы переключаете страницу, вам нужно запросить сервер для рендеринга, а после рендеринга сервера возвращайте HTML в клиент для отображения.
2. Связанные данные динамические. Это означает, что изменения в модели данных будут немедленно отражены на странице. Этот эффект объясняется двусторонним механизмом связывания, реализованным нокаутированием.
Использование этого метода также прост для разработки программы. Веб-API отвечает только за предоставление данных, а на передней странице также снижается много операций DOM. Потому что операции DOM сложны и подвержены ошибкам. Это также означает уменьшение неявных ошибок в программе. Более того, мобильные телефоны, веб-браузеры и несколько платформ могут использоваться в сервисе, чтобы избежать повторной разработки.
5. Резюме
На данный момент будет введено введение этой статьи. В этой статье в основном представлены использование нокаутов для завершения программы спа. Фактически, в реальной работе модель создания одностраничных программ больше основана на AngularJ. Тогда есть много нокаутирования, но nockoutjs-это просто структура MVVM, и его механизм маршрутизации должен использоваться с некоторыми другими классовыми библиотеками, такими как механизм маршрутизации в ASP.NET MVC здесь, вы также можете использовать фронтальную структуру Director.js. По сравнению с nockoutjs, AngularJS является структурой MVVM+MVC. Таким образом, в следующей теме мы представим, как использовать AngularJS для создания программы одной страницы (SPA).
Скачать все исходные коды в этой статье: Spawithnockoutjs
Если вы все еще хотите учиться глубже, вы можете нажать здесь, чтобы изучить и прикрепить к вам 3 захватывающих тем:
Начальная учебник по обучению
Bootstrap Practical Rutorial
Учебное пособие по использованию плагина Bootstrap
Выше приведено в этой статье, я надеюсь, что это будет полезно для каждого обучения.