Несколько способов ускорить приложение на ASP.NET WebForms

в 6:31, , рубрики: .net, AdvantShop, ASP.NET, webforms, Блог компании AdVantShop.NET, метки: , ,

Существует распространенное мнение, что приложения на ASP.NET WebForms очень медленные, тяжеловесные, подходят только для корпоративных порталов и в основном работают в интрасетях. Однако есть несколько способов существенно повысить скорость работы вашего приложения. Большинство этих способов не новы, применяются многими разработчиками. Здесь мы собрали те из них, которые хорошо себя зарекомендовали при разработке движка AdVantShop.NET Ultimate.

1. Отказываемся от UpdatePanel

Одним из самых главных факторов, тормозящих работу вэб-сайта на ASP.NET WebForms – использование контрола UpdatePanel. Для его работы требуется разместить на странице ScriptManager. В этом и заключается первая проблема.
Попробуем создать aspx-страничку и поместим простой Label внутри UpatePanel

<asp:ScriptManager runat="server" />
<asp:UpdatePanel ID="Updatepanel1" runat="server">
    <ContentTemplate>
        <asp:Label runat="server" Text="Hello world!" />
    </ContentTemplate>
</asp:UpdatePanel>

Запустим проект, посмотрим, во что это превратилось:

<script src="/WebSite1/WebResource.axd?d=ddsVPpuyrEEfoBbqjFYV9oHZ-YB8armGl03nreWb8sxbdd0MXlSne0kVoo7mheRQ1cxLip1L_44SDmzKsEIHZW930AWZ2Zs_ae9a9NTgNck1&t=634790730663322540" type="text/javascript"></script>
<script src="/WebSite1/ScriptResource.axd?d=x2K70A_qh5LLRPvqydlLAwONxKroPWgAGdmlhYHXFoXfUrRwcjMDwh9AgTRQkLVd0gEj8C6MhbyA_hq2RLgNpJxsoak2SQ5Dsi8RIcHR-k72-HxC0Vc2GfaaM_NqOWGS6pN4O31eNf9cYeLVlOWAro2fu6lKP46_8vn-OGH5mmN_8TmV6BQKdue5UaNBp45o0&t=ffffffff940d030f" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
if (typeof(Sys) === 'undefined') throw new Error('ASP.NET Ajax client-side framework failed to load.');
//]]>
</script>
<script src="/WebSite1/ScriptResource.axd?d=JmrJ_HQ4TGD_jchBEWNRb8OenR4k6OQ7VRyH_toBPQT3JUiqiQs3R0jW7v7zXJ2kEQZHsBtgkthfErRPRvwOKI-uadU-pRHAfl5iwxP1hhqLZX5DmrmatAe9-DtrY8UpvLZcFvWHClUe3sQQzRVLcMCbnpjYQ_AnnrUMVfpTPXHnstGsM4ZnlODzOKmhM5LU0&t=ffffffff940d030f" type="text/javascript"></script>
     <script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ctl02', 'form1', ['tUpdatepanel1','Updatepanel1'], [], [], 90, '');
//]]>
</script>
<div id="Updatepanel1">
    <span>Hello world!</span>
</div>

image

Видим, что для вывода простого текста внутри UpdatePanel потребовалось 2 кб html и еще почти 100 кб скриптов. Тоже самое без UpdatePanel занимает примерно 500 байт.
Вторая же проблема – жизненный цикл страницы. То, что полностью отсутствует в ASP.NET MVC, и то с чем приходится уживаться в ASP.NET WebForms. Подробнее отличия есть здесь.
При любом событии внутри UpdatePanel происходит PostBack и выполняются все события страницы, в которых могут происходить трудные вычисления или запросы к базе данных. Если не использовать кругом конструкцию вроде:

 if(!Page.IsPostBack)
 {
     Do something… 
 }

то приложение будет работать не так быстро, как хотелось бы.

Простой пример:

Пользователь вводит номер заказа и нужно вывести его текущий статус, без перезагрузки страницы целиком.

image

Как это можно сделать, не используя UpdatePanel.
Создаем класс обработчик http-запросов

public class CheckOrder : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
	context.Response.ContentType = "application/json";
       Order order = OrderService.GetOrder(context["number"]);
        if(order != null)
        {
            var statusResult = new
                               {
                                   Status = order.Status,
                                   Comment = order.Comment
                               };
            context.Response.Write(JsonConvert.SerializeObject(
statusResult));
        }   
    }
    
    public bool IsReusable
    {
        get { return true;}
    }
}

Тут еще пригодится библиотека Newtonsoft.Json для сериализации объектов в JSON. Но можно обойтись и без нее возвращая просто текст.
Далее создаем js функцию, которая при помощи jquery при нажатии на кнопку «Проверить» будет отправлять через ajax get запрос на получение статуса.

function CheckOrder(ordernum) {
    $.ajax({
        async: false,
        dataType: "json",
        cache: false,
        url: "checkorder.ashx",
        data: { number: ordernum },
        success: function (status) {
            $("#orderStatus").text(status.Status);
     $("#orderComment").text(status.Comment);
        },
        error: function () {
$("#orderStatus").text("Ошибка получения статуса"));
        }
    });
}

Все очень просто, но эффективно. Также вместо httphandler можно обойтись статическим методом в классе страницы. Для этого достаточно пометить его атрибутом [WebMethod].

2. Избавляемся от ViewState

ViewState – еще одна особенность WebForms. У нее есть свои плюсы и минусы, которые хорошо описаны в этой статье. Однако в большинстве случаев без него можно обойтись. В старых версиях AdvantShop.Net ViewState на разных страницах был от 15 до 30 кб, что было очень неприятным, поскольку эти килобайты пересылались на каждом обращении к страничке. Как можно этого если не избежать, то хотя бы минимизировать риски?
Достаточно всего лишь прописать на Мастер-Странице

<%@ Master Language="C#" CodeFile="MasterPage.master.cs" Inherits="MasterPage" EnableViewState="false" ViewStateMode="Disabled" %>

Однако начнутся проблемы с такими контролами, которым необходим ViewState для хранения своего состояния – например, DropDownList или ListView. В таких случаях придется ViewState все же включить, но его можно будет отключить для каждого контрола, который его не использует. Ссылка для заинтересовавшихся.

3. Уходим от стандартных контролов и AjaxControlToolkit

Так сложилось, что в Microsoft при разработке контролов для ASP.NET не особо задумывались о чистоте и оптимальности верстки, количестве запросов и скорости работы этих самых контролов. GridView — очень медленный, TreeView генерирует ужасную верстку, Calendar труден в настройке и захламляет html. Те, кто ими пользовались, могли сами в этом убедиться.
Простой выход использовать jquery плагины или любые другие js фреймворки.

4. Минимизируем контент страниц

Чтобы повысить скорость работы сайта необходимо уменьшить количество http запросов к серверу и максимально сжать статический контент страниц: изображения, css и js файлы. С изображениями тут все понятно – используем выше степень сжатия и спрайты. Css и js файлы можно сжать с помощью специальных минимизаторов. Однако это неудобно, приходится хранить две версии: полную — для редактирования и дебага, и минимизированную — для продакшена.
Другой способ – использовать библиотеку SquishIt. Как это делать, можно почитать тут. В результате комбинируем и минимизируем все js и css в два файла, уменьшаем количество запросов и увеличиваем время загрузки страницы примерно на две секунды.
До применения:

image
После:

image
5. Сжимаем HTML
Большинство браузеров поддерживает сжатие контента GZip либо Deflate. Создаем класс, который будет выполнять сжатие.

namespace AdvantShop.Core.Compress
{
    public class CompressContent : IHttpModule
    {
        public void Dispose()
        {
            // Nothing to dispose; 
        }
        public void Init(HttpApplication context)
        {
            context.PreRequestHandlerExecute += PreRequestHandlerExecute;
        }

        private static void PreRequestHandlerExecute(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            HttpRequest request = app.Context.Request;
            HttpResponse response = app.Context.Response;

            if (request.FilePath.Contains(".aspx") || request.FilePath.Contains(".js") || request.FilePath.Contains(".css"))
            {
                if (!(request.Browser.IsBrowser("IE") && request.Browser.MajorVersion <= 6)) // IE6 does not support compression 
                {
                    string acceptEncoding = request.Headers[HttpConstants.HttpAcceptEncoding];
                    if (!string.IsNullOrEmpty(acceptEncoding))
                    {
                        acceptEncoding = acceptEncoding.ToLower(CultureInfo.InvariantCulture);
                        if (acceptEncoding.Contains(HttpConstants.HttpContentEncodingGzip))
                        {
                            response.Filter = new HttpCompressStream(response.Filter, CompressionMode.Compress, HttpCompressStream.CompressionType.GZip);
                            response.AddHeader(HttpConstants.HttpContentEncoding, HttpConstants.HttpContentEncodingGzip);
                        }
                        else if (acceptEncoding.Contains(HttpConstants.HttpContentEncodingDeflate))
                        {
                            response.Filter = new HttpCompressStream(response.Filter, CompressionMode.Compress, HttpCompressStream.CompressionType.Deflate);
                            response.AddHeader(HttpConstants.HttpContentEncoding, HttpConstants.HttpContentEncodingDeflate);
                        }
                    }
                }
                else
                {
                    response.Filter = new HttpCompressStream(response.Filter, CompressionMode.Compress, HttpCompressStream.CompressionType.None);
                }
            }
        }
    }
}

Затем, чтобы подключить этот модуль в свое приложение просто добавим в web.config строчки:

<modules>
<add name="CompressContent" type="AdvantShop.Core.Compress.CompressContent"/>
  </modules>

Получаем очень хороший результат – сжатие текста в четыре раза. Причем сжимается не только html но также js и css.
До:
image
После:
image
6. Кешируем UserControls
Чтобы закешировать User Control, достаточно поместить в заголовок контрола строку вида:

<%@ OutputCache Duration="60" VaryByParam="CategoryID" %>

Таким образом, мы указываем, что контрол будет храниться в кеше 60 секунд и создастся своя версия кеша в зависимости от параметра из Request[“CategoryID”].
Вместо заключения
Это только основные методы ускорения, которые в большей степени применимы к WebForms. Однако не стоит забывать и про другие методы, особенно те, на которые указывает эта полезная тулза — PageSpeed.

Автор: buxler

Поделиться

* - обязательные к заполнению поля