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

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

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

В самом деле, используется только связка ~/Content/css, потому что она загружает файл /Content/Site.css, который был изначально определен в связке, и все файлы CSS в папке /Content в соответствии с изменениями, которые мы сделали в листинге 24-9. Чтобы создать необходимое нам поведение, мы отредактировали файл _Layout.cshtml, чтобы использовать наши новые связки и удалить ненужные ссылки, как показано в листинге 24-10.

Листинг 24-10: Добавляем нужные связки в файл _Layout.cshtml

<!DOCTYPE html> <html>

<head>

<meta charset="utf-8" />

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

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

</head>

<body>

@RenderBody()

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

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

</html>

Чтобы увидеть HTML, который генерируют эти вспомогательные методы, запустите приложение, перейдите по ссылке /Home/MakeBooking и просмотрите исходный код страницы. Вот вывод метода

Styles.Render для связки ~/Content/css:

<link href="/Content/CustomStyles.css" rel="stylesheet"/> <link href="/Content/Site.css" rel="stylesheet"/>

А вот вывод метода Scripts.Render:

<script src="/Scripts/jquery-1.7.1.js"></script>

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

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

Используем секции scripts

Осталось еще одно изменение. Выполнение нашего специфического для представления JavaScript, который содержится в файле /Scripts/Home/MakeBooking.js, зависит от jQuery, которая должна создать обработчик события для кнопки. Следовательно, мы должны гарантировать, что файл jQuery загружается перед MakeBooking.js. Если вы посмотрите на макет в листинге 24-10, то увидите, что вызов метода RenderBody находится перед вызовом метода Scripts.Render, а это значит, что элемент script в представлении появляется перед элементами script в макете, и код кнопки не будет работать. (Он либо не создаст оповещения, либо сообщит об ошибке JavaScript, в зависимости от используемого браузера).

Это можно исправить, переместив вызов Scripts.Render в элемент head в представлении; по сути, так мы обычно и делаем. Тем не менее, мы можем также использовать дополнительную секцию scripts, которая определена в файле _Layout.cshtml и которую вы можете увидеть в листинге 2410. В листинге 24-11 показано, как мы обновили представление MakeBooking.cshtml, чтобы использовать эту секцию.

621

Листинг 24-11: Используем дополнительную секцию scripts в представлении

@model ClientFeatures.Models.Appointment

@{

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

{

OnSuccess = "processResponse" };

}

<h4>Book an Appointment</h4>

@section scripts {

<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>

Секция scripts появляется после вызова Scripts.Render в макете, что означает, что наш специфический для представления скрипт не будет загружен до jQuery, и наш элемент button будет работать так, как мы планировали. При использовании связок эта ошибка возникает очень часто, и именно поэтому мы явно ее продемонстрировали.

Измеряем эффект изменений

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

Для этого мы очистили кэш браузера, перешли по ссылке /Home/MakeBooking и отследили запросы, которые были отправлены браузером, с помощью утилит F12. Результат показан на рисунке 24-3.

622

Рисунок 24-3: Измеряем производительность приложения, в котором используются связки

Вот краткая информация о производительности:

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

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

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

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

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

Это не плохо. Мы уменьшили объем данных, загружаемый браузером, примерно на 30 процентов. Но результаты станут еще лучше, если мы переключим приложение из режима отладки на развертывание. Для этого необходимо установить атрибуту debug в элементе compilation файла Web.config значение false, как показано в листинге 24-12.

Листинг 24-12: Отключаем режим отладки в файле Web.config

<system.web>

<httpRuntime targetFramework="4.5" /> <compilation debug="true" targetFramework="4.5" />

Когда это изменение внесено, мы перезапустим приложение, очистим кэш браузера и измерим сетевые запросы снова. Результат показан на рисунке 24-4.

Рисунок 24-4: Измеряем производительность приложения, в котором используются связки, в режиме развертывания

623

Вот краткая информация о производительности:

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

Был отправлен только 1 запрос к CSS.

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

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

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

Вам может стать интересно, почему уменьшилось число запросов к файлам CSS и JavaScript. Причина в том, что MVC Framework объединяет и минимизирует таблицы стилей и файлы JavaScript в режиме развертывания, так что все содержимое связки может быть загружено с помощью одного запроса. Чтобы увидеть, как это работает, посмотрите на HTML, который создает приложение. Вот код, сгенерированный методом Styles.Render:

<link href="/Content/css?v=6jdfBoUlZKSHjUZCe_rkkh4S8jotNCGFD09DYm7kBWE1" rel="stylesheet"/>

А вот код, сгенерированный методом Scripts.Render:

<script

src="/bundles/clientfeaturesscripts?v=SOAJUpvGwNr0XsILBsLrEwEpdyIziIN9frqxIjgTyWE1">

</script>

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

Проведенная оптимизация производит значительный эффект. Браузер отправляет гораздо меньше запросов, что уменьшает объем данных, передаваемых клиенту. И мы передаем меньше данных обратно на сервер; в самом деле, мы отправили около 27% от того объема данных, который был зафиксирован при первом измерении производительности в начале этой главы.

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

Работа с мобильными устройствами

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

624

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

Подсказка

Когда вы создаете новый проект MVC Framework, доступна опция Mobile template, но это всего лишь вариант шаблона Internet, в котором добавлена поддержка

библиотеки jQuery Mobile. Нам очень нравится jQuery Mobile, и, как вы уже догадались, Адам подробно описал ее в других своих книгах. Однако нам не нравится шаблон Mobile, поскольку он добавляет стандартные представления и

контроллеры для аутентификации, которые, по нашему мнению, не очень хорошо работают. Вместо этого мы рекомендуем вам установить jQuery Mobile и другие необходимые библиотеки JavaScript с помощью NuGet.

Пересматриваем приложение

Лучше всего начать оптимизировать приложение для мобильных устройств с самого начала разработки проекта. Здесь мы будем делать все наоборот, а именно - добавим поддержку в существующее приложение ClientFeatures, на примере которого мы продемонстрировали, как использовать связки.

Для начала попытаемся понять, как наше приложение выглядит на различных мобильных устройствах. Для этого мы будем использовать эмулятор Opera Mobile Emulator, который можно бесплатно скачать с www.opera.com/developer/tools/mobile. Конечно, ничем нельзя заменить тестирование мобильного приложения на реальных мобильных устройствах, но Opera Mobile Emulator позволит быстро оценить, где мы находимся. Он распространяется бесплатно, обеспечивает верную эмуляцию браузера Opera Mobile, который используется на многих мобильных устройствах, и позволяет протестировать различные профили и возможности аппаратного обеспечения.

Подсказка

Есть другие эмуляторы, в том числе для платформ Windows Phone, Android и Blackberry. Как правило, они работают довольно медленно и трудны в использовании, потому что они эмулируют целую мобильную операционную систему, а не только браузер. Мы еще не нашли ни одного вменяемого эмулятора iPhone для Windows PC: в них обычно используется Windows-версия Safari, которая отличается от версии IOS и похожа на неудачную копию браузера Apple.

На рисунке 24-5 показано, как отображается в мобильном браузере представление /Home/MakeBooking. В эмуляторе мы использовали профиль HTC Hero, который имитирует типичное, ничем не выдающееся мобильное устройство с сенсорным дисплеем 320 × 480 пикселей и разрешением 180 пикселей на дюйм.

625

Рисунок 24-5: Приложение в мобильном браузере

Так как это очень простое приложение, оно не так уж плохо отображается на экране. Если придираться, то можно сказать, что элементы расположены не лучшим образом для использования на сенсорном экране. Большинство реальных приложений, разработанных и протестированных в браузерах для РС, в мобильных браузерах выглядят ужасно.

Используем специальные макеты и представления для мобильных устройств

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

Вкачестве примера мы создали новый файл представления под названием

Views/Home/MakeBooking.Mobile.cshtml. Обратите внимание на фрагмент .Mobile перед расширением файла. Содержимое представления показано в листинге 24-13.

Листинг 24-13: Представление /Views/Home/MakeBooking.Mobile.cshtml

@model ClientFeatures.Models.Appointment

@{

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

{

OnSuccess = "processResponse" };

}

<h4>This is the MOBILE View</h4>

@section scripts {

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

}

626

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

{

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

<p>@Html.EditorFor(m => m.TermsAccepted) 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>

Мы внесли небольшие коррективы в это представление, чтобы метки для элементов input отображались отдельно, а также изменили содержание элемента h4 title, чтобы стало очевидно, какое представление отображается.

MVC Framework применяет мобильное представление автоматически. Если вы запустите приложение и перейдете по ссылке /Home/MakeBooking в браузере настольного компьютера, то увидите HTML, сгенерированный для представления /Views/Home/MakeBooking.cshtml. Но если вы перейдете по той же ссылке в эмуляторе мобильного браузера, то увидите HTML, сгенерированный для представления /Views/Home/MakeBooking.Mobile.cshtml, как показано на рисунке 24-6.

Рисунок 24-6: Переход по ссылке в настольном и мобильном браузерах

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

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\Browsers, но в других операционных системах директория может отличаться. Здесь находится ряд файлов, каждый из которых идентифицирует мобильный браузер – есть файл и для Opera Mobile. Когда MVC Framework получает запрос от мобильного эмулятора, он смотрит на строку UserAgent, которую отправляют все браузеры, и определяет, что запрос был отправлен с мобильного устройства; далее используется представление MakeBooking.Mobile.cshtml.

627

Создаем пользовательские режимы отображения

По умолчанию MVC Framework обнаруживает только мобильные устройства и рассматривает их в одной категории. Если вы хотите уточнить, как отвечать различным типам устройств, то можете создать свои собственные режимы отображения. Чтобы это продемонстрировать, мы будем использовать профиль Amazon Kindle Fire, который имеется в Opera Mobile Emulator. Opera Mobile,

установленная на планшете, посылает строку UserAgent, которая не соответствует тому, что ожидает

.NET Framework от Opera Mobile; таким образом, к устройству отправляется стандартное представление MakeBooking.cshtml.

В листинге 24-14 показано, как мы изменили файл Global.asax, чтобы создать новый режим отображения для браузера Opera Tablet.

Листинг 24-14: Создаем новый режим отображения в файле Global.asax

using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Http; using System.Web.Mvc;

using System.Web.Optimization; using System.Web.Routing; using System.Web.WebPages;

namespace ClientFeatures

{

public class MvcApplication : System.Web.HttpApplication

{

protected void Application_Start()

{

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("OperaTablet")

{

ContextCondition = (context => context.Request.UserAgent.IndexOf ("Opera Tablet", StringComparison.OrdinalIgnoreCase) >= 0)

});

AreaRegistration.RegisterAllAreas();

WebApiConfig.Register(GlobalConfiguration.Configuration);

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

RouteConfig.RegisterRoutes(RouteTable.Routes);

BundleConfig.RegisterBundles(BundleTable.Bundles);

}

}

}

Статическое свойство DisplayModeProvider.Instance.Modes возвращает коллекцию, с помощью которой мы можем определить пользовательские режимы отображения; для этого необходимо создать экземпляр объекта DefaultDisplayMode и установить в качестве значения свойства ContextCondition лямбда-выражение, которое получает объект HttpContextBase и возвращает логическое значение. Аргументом конструктора для класса DefaultDisplayMode является имя режима отображения, которое будет использоваться для поиска макетов и представлений, адаптированных для конкретного типа устройств. Мы указали OperaTablet, что означает, что MVC Framework будет искать такие представления, как /Views/Home/MakeBooking.OperaTable.cshtml.

628

Подсказка

Обратите внимание, что мы использовали метод Insert, чтобы поместить объект DefaultDisplayMode с нулевым индексом в коллекции, которую возвращает свойство DisplayModeProvider.Instance.Modes. MVC Framework проверяет

режимы отображения по очереди и останавливает поиск, если выражение ContextCondition какого-либо режима возвращает true. Есть резервный режим

отображения, который будет соответствовать любому запросу и использовать представление по умолчанию (т.е. без дополнений OperaTable или Mobile). Мы

должны гарантировать, что наш режим отображения находится в очереди перед резервным вариантом.

Мы используем объект HttpContextBase, чтобы решить, соответствует ли полученный запрос искомому режиму отображения. Мы проверяем, содержит ли свойство Request.UserAgent значение Opera Tablet, и если да, то возвращаем true.

Чтобы воспользоваться этим режимом, мы создали новое представление под названием

/Views/Home/MakeBooking.OperaTablet.cshtml, которое показано в листинге 24-15.

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

@model ClientFeatures.Models.Appointment

@{

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

{

OnSuccess = "processResponse" };

}

<h4>This is the OPERA TABLET View</h4>

@section scripts {

<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>Name:</p><p>@Html.EditorFor(m => m.ClientName)</p> <p>@Html.ValidationMessageFor(m => m.Date)</p> <p>Date:</p><p>@Html.EditorFor(m => m.Date)</p> <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p>

<p>@Html.EditorFor(m => m.TermsAccepted) 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>

629

Здесь мы внесли только одно изменение - в элемент h4, который сообщает нам, какое используется представление. Если мы запустим Opera Mobile Emulator с профилем Kindle File и перейдем по ссылке /Home/MakeBooking, то увидим, что используется наше новое представление, как показано на рисунке 24-7.

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

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

Внимание!

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

Резюме

В этой главе мы рассмотрели связки и режимы отображения, которые могут быть полезны при разработке клиентской части веб-приложения. В следующей главе мы познакомим вас с Web API, с помощью которого можно легко создавать веб-сервисы для клиентов.

630

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