Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

.pdf
Скачиваний:
25
Добавлен:
19.03.2016
Размер:
17.66 Mб
Скачать

Подсказка

Минимизированные файлы JavaScript по-прежнему содержат текст, но его очень трудно читать. Чтобы понять, что мы имеем в виду, просто откройте одну из минимизированных библиотек и просмотрите ее содержимое (это можно сделать из Visual Studio).

Недостатком минимизированных файлов является то, что они затрудняют поиск проблем, поэтому во время разработки, как правило, используются обычные файлы, которые затем заменяются минимизированными версиями во время развертывания приложения.

Минимизированные версии файлов, как правило, содержат в имени фрагмент .min; например, минимизированная версия jQuery-1.7.1.js – это файл jQuery-1.7.1.min.js. Однако не все библиотеки следуют этому соглашению; например, файл knockout-2.1.0.js является минимизированным, а knockout-2.1.0.debug.js содержит читаемый код.

MVC Framework содержит несколько полезных функций, которые помогут вам управлять файлами JavaScript, которые использует ваше приложение. Теперь, когда мы познакомились с файлами скриптов и тем, что они делают, перейдем к созданию примера приложения, в котором затем и продемонстрируем эти функции управления.

Подготовка приложения для примера

Для этой главы мы создали новый проект MVC под названием ClientFeatures на шаблоне Basic. Мы будем работать с приложением, аналогичным приложению из предыдущей главы, поэтому для начала создадим класс под названием Appointment.cs в папке Models. Содержимое этого файла показано в листинге 24-1.

Листинг 24-1: Класс модели Appointment

using System;

using System.ComponentModel.DataAnnotations;

namespace ClientFeatures.Models

{

public class Appointment

{

[Required]

public string ClientName { get; set; }

[DataType(DataType.Date)]

public DateTime Date { get; set; }

public bool TermsAccepted { get; set; }

}

}

Мы создали контроллер Home, который работает с классом модели Appointment; он показан в листинге 24-2.

Листинг 24-2: Контроллер Home в приложении ClientFeatures

using System;

using System.Web.Mvc;

using ClientFeatures.Models;

611

namespace ClientFeatures.Controllers

{

public class HomeController : Controller

{

public ViewResult MakeBooking()

{

return View(new Appointment

{

ClientName = "Adam",

Date = DateTime.Now.AddDays(2), TermsAccepted = true

});

}

[HttpPost]

public JsonResult MakeBooking(Appointment appt)

{

//statements to store new Appointment in a

//repository would go here in a real project return Json(appt, JsonRequestBehavior.AllowGet);

}

}

}

Вэтом контроллере есть две версии метода MakeBooking. Версия без параметров создает объект Appointment и передает его в метод View, который визуализирует представление по умолчанию. HttpPost версия метода MakeBooking ждет, когда механизм связывания создаст объект Appointment, кодирует его в методе Json и отправляет его обратно к клиенту в формате JSON.

Вэтой главе мы уделяем основное внимание функциям MVC Framework, которые поддерживают клиентскую разработку, так что мы немного сократили код контроллера, чего не стали бы делать в реальном проекте. Обратите внимание, что мы не выполняем никакой валидации, когда получаем запрос HTTP POST, и просто отправляем данные объекта, созданного механизмом связывания, в браузер в формате JSON (без поддержки ответов HTML на запросы POST).

Мы хотим максимально упростить использование формы с Ajax, которую мы определили в файле /Views/Home/MakeBooking.cshtml; он показан в листинге 24-3. Мы сосредоточимся на скриптах и элементах ссылок в представлении, так что взаимодействие с приложением отойдет на второй план.

Листинг 24-3: Представление MakeBooking

@model ClientFeatures.Models.Appointment

@{

ViewBag.Title = "Make A Booking"; AjaxOptions ajaxOpts = new AjaxOptions

{

OnSuccess = "processResponse" };

}

<h4>Book an Appointment</h4>

<link rel="stylesheet" href="~/Content/CustomStyles.css" /> <script src="~/Scripts/jquery-1.7.1.min.js"></script> <script src="~/Scripts/jquery.validate.js"></script>

<script src="~/Scripts/jquery.validate.unobtrusive.js"></script> <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>

<script type="text/javascript"> function processResponse(appt) {

$('#successClientName').text(appt.ClientName);

612

$('#successDate').text(processDate(appt.Date)); switchViews();

}

function processDate(dateString) {

return new Date(parseInt(dateString.substr(6, dateString.length - 8))).toDateString();

}

function switchViews() { var hidden = $('.hidden');

var visible = $('.visible'); hidden.removeClass("hidden").addClass("visible"); visible.removeClass("visible").addClass("hidden");

}

$(document).ready(function () { $('#backButton').click(function (e) {

switchViews();

});

});

</script>

<div id="formDiv" class="visible"> @using (Ajax.BeginForm(ajaxOpts))

{

@Html.ValidationSummary(true) <p>@Html.ValidationMessageFor(m => m.ClientName)</p> <p>Your name: @Html.EditorFor(m => m.ClientName)</p> <p>@Html.ValidationMessageFor(m => m.Date)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>

<p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p> <input type="submit" value="Make Booking" />

}

</div>

<div id="successDiv" class="hidden"> <h4>Your appointment is confirmed</h4>

<p>Your name is: <b id="successClientName"></b></p>

<p>The date of your appointment is: <b id="successDate"></b></p> <button id="backButton">Back</button>

</div>

В этом представлении есть два элемента div. Первый будет показан пользователю, когда представление будет визуализировано впервые; он содержит форму с Ajax. Когда форма отправлена, ответ сервера на запрос Ajax скроет форму и отобразит другой элемент div, в котором мы отображаем информацию о подтверждении записи на прием.

Мы включили в это представление ряд стандартных библиотек JavaScript из папки Scripts и определили локальный элемент script, который содержит простой код jQuery, специфичный для этого представления. Мы также добавили элемент link, который загружает из папки /Content файл CSS под названием CustomStyles.css; он показан в листинге 24-4.

Листинг 24-4: Содержимое файла CustomStyles.css

div.hidden { display: none;} div.visible { display: block;}

Мы хотим создать типичный сценарий для сложного представления, не усложняя при этом приложение. Именно поэтому мы добавили файл CSS, который содержит только два стиля, и используем столько библиотек jQuery для такого простого представления. Основная идея в том, что

613

нам придется управлять большим количеством файлов. Когда вы будете работать с реальными приложениями, вы будете поражены, как много файлов скриптов и стилей используют ваши представления.

Чтобы увидеть, как работает наш пример приложения, запустите его и перейдите по ссылке /Home/MakeBooking. Форма предварительно заполнена данными, так что вы можете просто нажать кнопку Make Booking, чтобы данные формы были отправлены на сервер с помощью Ajax. Когда будет получен ответ, вы увидите информацию об объекте Appointment, который был создан механизмом связывания из данных формы, а также кнопку, которая вернет вас обратно к форме, как показано на рисунке 24-1.

Рисунок 24-1: Используем пример приложения

Управление скриптами и таблицами стилей

Представление, которое мы создали в листинге 24-3, является типичным для реальных проектов MVC. Оно содержит набор библиотек из папки Scripts, таблицы стилей CSS из папки Content, локальные элементы script и, конечно, разметку HTML и Razor.

Разработчики обычно пишут файлы представлений так же, как бы они писали страницы HTML; это нормальный подход, но не самый эффективный. Как мы покажем вам в следующих разделах, в представлении MakeBooking.cshtml есть скрытые проблемы. Можно сделать ряд улучшений относительно того, как мы управляем скриптами и таблицами стилей.

Измеряем время загрузки скриптов и таблиц стилей

Если вы рассматриваете возможность оптимизации в любом проекте, начинать необходимо с измерений. Мы обеими руками за эффективность и оптимизацию приложений, но, как показывает опыт, из-за спешки и стремления оптимизировать совершенно незначительные проблемы, разработчики реализуют решения, которые вызывают проблемы в будущем.

Для проблем, которые мы рассмотрим в этой главе, мы будем выполнять измерения с помощью утилит F12 из Internet Explorer 10 (называемых так потому, что к ним можно получить доступ, нажав клавишу F12). Загрузите приложение, перейдите по ссылке /Home/MakeBooking и нажмите клавишу F12. Когда откроется окно утилит, перейдите на вкладку Network и нажмите кнопку Start

614

Capturing. Обновите содержимое вкладки браузера (щелкните правой кнопкой мыши в окне браузера и выберите Refresh), и вы увидите результаты, показанные на рисунке 24-2.

Рисунок 24-2: Измеряем время загрузки скриптов и таблиц стилей для примера приложения

Утилиты F12 в IE10 позволяют измерять производительность для сетевых запросов, которые отправляет ваше приложение. (Если вы не используете Internet Explorer, можно найти и другие инструменты. Наш любимый - Fiddler, который вы можете скачать бесплатно с www.fiddler2.com.)

Чтобы сравнивать результаты оптимизации, которую мы проведем в этой главе, мы будем использовать данные, приведенные на рисунке 24-2, как отправную точку. Вот ключевые цифры:

Браузер сделал 9 запросов по ссылке /Home/MakeBooking

Было отправлено 2 запроса к файлам CSS.

Было отправлено 6 запросов к файлам JavaScript.

В общей сложности 2,978 байт было отправлены от браузера к серверу.

В общей сложности 470,417 байт было отправлено от сервера к браузеру.

Это наихудшие данные о производительности для нашего приложения, так как перед тем, как перезагрузить представление, мы очистили кэш браузера. Мы сделали это, чтобы провести измерения для конкретной отправной точки, хотя мы и знаем, что в реальных условиях результаты были бы лучше, так как браузер кэширует файлы для предыдущих запросов.

Если мы перезагрузим страницу /Home/MakeBooking без очистки кэша, то мы получим следующие результаты:

Браузер сделал 9 запросов по ссылке /Home/MakeBooking

Было отправлено 2 запроса к файлам CSS.

Было отправлено 6 запросов к файлам JavaScript.

В общей сложности 2,722 байт было отправлены от браузера к серверу.

В общей сложности 6,302 байт было отправлено от сервера к браузеру.

Это наш лучший сценарий, где все запросы к файлам CSS и JavaScript были обслужены с помощью ранее закэшированных файлов.

615

Примечание

В реальном проекте мы бы остановились и начали разбираться, есть ли здесь проблема, которую можно оптимизировать. Может показаться, что 470K – слишком много пропускной способности для простой веб-страницы, но все зависит от условий. Мы могли бы разрабатывать приложение для Интранет, где канал дешевый и широкий, и любая оптимизаций будет неоправданна из-за стоимости труда разработчика, который мог бы работать над более важным проектом. Равным образом, мы могли бы быть писать приложение, которое работает через интернет с важными для нас клиентами, у которых низкая скорость соединения; в этом случае стоит потратить время на оптимизацию каждого аспекта приложения. Важно то, что вы не должны автоматически оптимизировать в приложении все, что можно – чаще всего вы сможете найти себе более полезное занятие. (Особенно если вы украдкой проводите оптимизацию приложения, никому ничего не сказав. Скрытая оптимизация - это плохая идея, и конечном счете она только навредит вашей работе.)

Если вы посмотрите на список файлов JavaScript, которые загружаются для представления, то заметите, что мы воссоздали две очень распространенных проблемы. Первая заключается в том, что мы смешали минимизированные и обычные файлы JavaScript. Это не очень большая проблема, но из-за нее отладка кода библиотеки в процессе разработки будет затруднена.

Второй проблемой является то, что мы загружаем и минимизированные, и обычные версии библиотек jQuery. Так происходит потому, что макет тоже загружает файлы JavaScript и CSS, и из-за отсутствия координации браузеру приходится загружать код, который у него уже есть.

Эти проблемы встречаются довольно часто. Далее мы рассмотрим функции MVC Framework, с помощью которых можно контролировать файлы скриптов и CSS. Мы также покажем вам, как сократить количество запросов, которое браузер должен отправить к серверу, и объем данных, который он должен загрузить.

Использование связок скриптов и стилей

Первым делом мы объединим наши файлы JavaScript и CSS в связки, что позволит работать с ними как с одним элементом. Связки определяются в файле /App_Start/BundleConfig.cs. В листинге 24показано содержимое этого файла по умолчанию, которое создается Visual Studio.

Листинг 24-5: Содержимое файла BundleConfig.cs по умолчанию

using System.Web;

using System.Web.Optimization;

namespace ClientFeatures

{

public class BundleConfig

{

public static void RegisterBundles(BundleCollection bundles)

{

bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js"));

bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include( "~/Scripts/jquery-ui-{version}.js"));

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(

616

"~/Scripts/jquery.unobtrusive*",

"~/Scripts/jquery.validate*"));

bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( "~/Scripts/modernizr-*"));

bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css")); bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(

"~/Content/themes/base/jquery.ui.core.css",

"~/Content/themes/base/jquery.ui.resizable.css",

"~/Content/themes/base/jquery.ui.selectable.css",

"~/Content/themes/base/jquery.ui.accordion.css",

"~/Content/themes/base/jquery.ui.autocomplete.css",

"~/Content/themes/base/jquery.ui.button.css",

"~/Content/themes/base/jquery.ui.dialog.css",

"~/Content/themes/base/jquery.ui.slider.css",

"~/Content/themes/base/jquery.ui.tabs.css",

"~/Content/themes/base/jquery.ui.datepicker.css",

"~/Content/themes/base/jquery.ui.progressbar.css",

"~/Content/themes/base/jquery.ui.theme.css"));

}

}

}

Статический метод RegisterBundles вызывается из метода Application_Start файла Global.asax

при первом запуске приложения MVC Framework. Метод RegisterBundles принимает объект BundleCollection, в котором мы регистрируем новые связки файлов с помощью метода Add.

Подсказка

Классы, которые используются для создания связок, содержатся в пространстве имен System.Web.Optimization; во время написания этой книги документацию

MSDN API для этого пространства имен было не так легко найти. Чтобы узнать больше о классах этого пространства имен, воспользуйтесь ссылкой http://msdn.microsoft.com/en-us/library/system.web.optimization.aspx.

Мы можем создавать связки для скриптов и таблиц стилей, но имейте в виду, нельзя объединять эти типы файлов, так как MVC Framework оптимизирует их по-разному. Стили представлены в классе

StyleBundle, скрипты – в классе ScriptBundle.

Чтобы создать новую связку, нужно создать экземпляр либо StyleBundle, либо ScriptBundle, оба из которых принимают единственный аргумент конструктора – это маршрут для ссылки на связку. Этот маршрут будет использоваться в качестве URL, по которому браузер будет запрашивать содержимое связки; поэтому важно использовать схему маршрута, чтобы он не конфликтовал с другими маршрутами в приложении. Самый безопасный способ это сделать - начать маршрут с ~/bundles или ~/Content. (Вы поймете, как это важно, когда мы объясним принцип работы связок).

Когда вы создали объект StyleBundle или ScriptBundle, добавьте в связку информацию о таблицах стилей и скриптах с помощью метода Include. Есть несколько интересных функций, которые позволяют сделать связки более гибкими. Чтобы продемонстрировать это, мы отредактировали и упростили связки нашего приложения в файле BundleConfig.cs, как показано в листинге 24-6. Мы добавили одну новую связку, отредактировали одну из существующих и удалили все те, которые нам не нужны.

Листинг 24-6: Настраиваем конфигурацию связок

using System.Web;

using System.Web.Optimization;

617

namespace ClientFeatures

{

public class BundleConfig

{

public static void RegisterBundles(BundleCollection bundles)

{

bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/*.css"));

bundles.Add(new ScriptBundle("~/bundles/clientfeaturesscripts")

.Include("~/Scripts/jquery-{version}.js", "~/Scripts/jquery.validate.js", "~/Scripts/jquery.validate.unobtrusive.js", "~/Scripts/jquery.unobtrusive-ajax.js"));

}

}

}

Сначала мы изменили связку StyleBundle с маршрутом ~/Content/css. Мы хотим, чтобы эта связка содержала все файлы CSS в нашем приложении, поэтому мы изменили аргумент, переданный в метод Include, с ~/Content/site.css (который ссылается на один файл) на ~/Content/*.css.

Символ звездочки (*) является универсальным, что означает, что теперь в нашей связке есть ссылка на все файлы CSS из папки /Content нашего проекта. Так можно гарантировать, что все файлы в папке будут автоматически включены в связку и порядок, в котором эти файлы будут загружены, не будет иметь значения. Так как для нас порядок загрузки CSS файлов не важен, мы можем использовать универсальный символ, но если вы будете полагаться на правила предшествования стилей CSS, то вам необходимо перечислить файлы по отдельности в определенном порядке.

Мы также добавили в файл BundleConfig.cs связку ScriptBundle с маршрутом ~/bundles/clientfeaturesscripts. Вскоре вы опять увидите маршруты для этих связок, когда мы будем применять их в приложении. Для этой связки мы использовали метод Include, в котором указали отдельные файлы JavaScript, разделенные запятыми, потому что нам нужны только некоторые файлы из папки Scripts и для нас важен порядок, в котором браузер загружает и выполняет код.

Обратите внимание, как мы указали файл библиотеки jQuery:

~/Scripts/jquery-{version}.js

В имени файла очень удобно использовать фрагмент {version}, потому что в таком случае он будет соответствовать любой версии указанного файла и в зависимости от конфигурации приложения выберет либо обычную, либо минимизированную версию файла. MVC 4 содержит версию библиотеки jQuery 1.7.1, что означает, что наша связка будет включать файл /Scripts/jQuery- 1.7.1.js в процессе разработки и /Scripts/jQuery-1.7.1.min.js при развертывании.

Подсказка

Выбор между обычной и минимизированной версией осуществляется на основе узла compilation в файле Web.config. Обычная версия будет использоваться, если атрибут debug содержит значение true, а минимизированная - если атрибут debug содержит false. Далее в этой главе мы переключим режим приложения с отладки

на развертывание, и вы сможете увидеть, как это происходит.

Преимущество использования {version} заключается в том, что вы можете обновить библиотеки до последних версий и не определять связки заново. Недостатком является то, что {version} не сможет различить две версии одной и той же библиотеки в одном каталоге. Так, например, если мы добавим

618

файл jQuery-1.7.2.js в папку Scripts, то клиент загрузит файлы 1.7.1 и 1.7.2. Чтобы это не подорвало нашу оптимизацию, мы должны убедиться, что в папке /Scripts находится только одна версия библиотеки.

Подсказка

MVC Framework достаточно умен, чтобы игнорировать файлы IntelliSense при обработке {version} в связке, но мы всегда проверяем то, что запрашивает

браузер, чтобы не включить нежелательные файлы. Вскоре мы покажем вам, как это сделать.

Применяем связки

Чтобы применить связки, сначала необходимо подготовить представление. Хотя этот шаг не является обязательным, он позволит MVC Framework выполнить максимальную оптимизацию нашего приложения. Мы создали новую папку /Scripts/Home и добавили в него файл JavaScript под названием MakeBooking.js. По соглашению, мы сохраняем файлы JavaScript для отдельных страниц в папках, названных по контроллеру. Содержимое файла MakeBooking.js показано в листинге 24-7.

Листинг 24-7: Содержимое файла MakeBooking.js

function processResponse(appt) { $('#successClientName').text(appt.ClientName); $('#successDate').text(processDate(appt.Date)); switchViews();

}

function processDate(dateString) {

return new Date(parseInt(dateString.substr(6, dateString.length - 8))).toDateString();

}

function switchViews() { var hidden = $('.hidden');

var visible = $('.visible'); hidden.removeClass("hidden").addClass("visible"); visible.removeClass("visible").addClass("hidden");

}

$(document).ready(function () { $('#backButton').click(function (e) {

switchViews();

});

});

Это тот же самый код, который использовался ранее - мы только переместили его в отдельный файл. Следующим шагом будет изменение представления /Views/Home/MakeBooking.cshtml: мы удалим элементы link и script, для которых была создана связка, как показано в листинге 24-8. Мы хотим, чтобы браузер запрашивал только необходимые файлы, и если мы оставим эти элементы, запросы будут дублироваться. Единственный оставшийся элемент script ссылается на специфичный для данного представления файл MakeBooking.js, который мы создали в листинге 24-7.

Листинг 24-8: Удаляем элементы link и script из представления MakeBooking.cshtml

@model ClientFeatures.Models.Appointment

@{

ViewBag.Title = "Make A Booking";

619

AjaxOptions ajaxOpts = new AjaxOptions

{

OnSuccess = "processResponse" };

}

<h4>Book an Appointment</h4>

<script src="~/Scripts/Home/MakeBooking.js" type="text/javascript"></script>

<div id="formDiv" class="visible"> @using (Ajax.BeginForm(ajaxOpts))

{

@Html.ValidationSummary(true) <p>@Html.ValidationMessageFor(m => m.ClientName)</p> <p>Your name: @Html.EditorFor(m => m.ClientName)</p> <p>@Html.ValidationMessageFor(m => m.Date)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>

<p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p> <input type="submit" value="Make Booking" />

}

</div>

<div id="successDiv" class="hidden"> <h4>Your appointment is confirmed</h4>

<p>Your name is: <b id="successClientName"></b></p>

<p>The date of your appointment is: <b id="successDate"></b></p> <button id="backButton">Back</button>

</div>

К связкам можно обращаться в файлах представлений, но мы, как правило, создаем связки только для контента, который является общим для нескольких представлений; это означает, что мы применяем связки в файлах макетов. В листинге 24-9 показан файл /Views/Shared/_Layout.cshtml, который добавила в проект Visual Studio.

Листинг 24-9: Макет, добавленный в проект Visual Studio

<!DOCTYPE html> <html>

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title>

@Styles.Render("~/Content/css")

@Scripts.Render("~/bundles/modernizr")

</head>

<body>

@RenderBody()

@Scripts.Render("~/bundles/jquery")

@RenderSection("scripts", required: false) </body>

</html>

Связки добавляются с помощью вспомогательных методов @Scripts.Render и @Styles.Render. Как видите, макет уже содержит три связки, которые выделены жирным шрифтом. Два вызова @Scripts.Render объясняют некоторые исходные данные профиля. Браузер запрашивает две копии библиотеки jQuery из-за связки ~/bundles/jQuery. Из-за связки ~/bundles/modernizr браузер запрашивает библиотеку, которую мы не используем в приложении.

620

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]