Попробуйте это:
Недавно я провел немало исследований и последующих разработок, которые позволяют значительно повысить производительность интерфейса нашего веб-приложения. Я думал, что поделюсь здесь основным решением.
Первое, что нужно сделать - это сравнить ваш сайт с помощью Yahoo YSlow и Google PageSpeed. Они будут выделены улучшения производительности "низко висящих фруктов". Если вы еще этого не сделали, в результате вы получите предложения по объединению, минимизации и распаковке статического контента.
Шаги, которые мы собираемся выполнить:
Напишите собственный HTTPHandler для объединения и минимизации CSS.
Напишите собственный HTTPHandler для объединения и минимизации JS.
Включите механизм, гарантирующий, что вышеперечисленное действует только тогда, когда приложение не находится в режиме отладки.
Напишите пользовательский веб-элемент управления на стороне сервера, чтобы легко поддерживать включение файла css / js.
Включите GZIP определенных типов контента на IIS 6.
Хорошо, давайте начнем с CSSHandler.asax, который реализует интерфейс .NET IHttpHandler:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
namespace WebApplication1
{
public class CssHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
string[] cssFiles = context.Request.QueryString["cssfiles"].Split(',');
List<string> files = new List<string>();
StringBuilder response = new StringBuilder();
foreach (string cssFile in cssFiles)
{
if (!cssFile.EndsWith(".css", StringComparison.OrdinalIgnoreCase))
{
//log custom exception
context.Response.StatusCode = 403;
return;
}
try
{
string filePath = context.Server.MapPath(cssFile);
string css = File.ReadAllText(filePath);
string compressedCss = Yahoo.Yui.Compressor.CssCompressor.Compress(css);
response.Append(compressedCss);
}
catch (Exception ex)
{
//log exception
context.Response.StatusCode = 500;
return;
}
}
context.Response.Write(response.ToString());
string version = "1.0"; //your dynamic version number
context.Response.ContentType = "text/css";
context.Response.AddFileDependencies(files.ToArray());
HttpCachePolicy cache = context.Response.Cache;
cache.SetCacheability(HttpCacheability.Public);
cache.VaryByParams["cssfiles"] = true;
cache.SetETag(version);
cache.SetLastModifiedFromFileDependencies();
cache.SetMaxAge(TimeSpan.FromDays(14));
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
}
}
Хорошо, теперь немного объяснений:
IsReUsable свойство:
Мы не имеем дело с чем-то специфичным для экземпляра, что означает, что мы можем безопасно повторно использовать один и тот же экземпляр обработчика для обработки нескольких запросов, потому что наш ProcessRequest является потокобезопасным. Подробнее.
Метод ProcessRequest:
Ничего особенного здесь не происходит. Мы перебираем предоставленные нам CSS-файлы (смотрите CSSControl ниже, чтобы узнать, как они поступают) и сжимаем каждый из них, используя порт .NET YUICompressor от Yahoo, перед добавлением содержимого в поток исходящих ответов. *
Оставшаяся часть метода посвящена настройке некоторых свойств кэширования HTTP для дальнейшей оптимизации способа загрузки клиентом браузера (или нет, в зависимости от обстоятельств).
Мы устанавливаем Etags в коде, чтобы они могли быть одинаковыми на всех машинах в нашей ферме серверов.
Мы устанавливаем зависимости Response и Cache для наших реальных файлов, поэтому, если они будут заменены, кеш будет признан недействительным.
Мы устанавливаем Cacheability так, чтобы прокси могли кешировать.
Мы используем VaryByParams, используя наш атрибут cssfiles, чтобы мы могли кешировать данные по каждой группе файлов CSS, переданной через обработчик.
А вот CSSControl, пользовательский серверный элемент управления, наследующий .NET LiteralControl.
Спереди:
<customcontrols:csscontrol id="cssControl" runat="server">
<CustomControls:Stylesheet File="main.css" />
<CustomControls:Stylesheet File="layout.css" />
<CustomControls:Stylesheet File="formatting.css" />
</customcontrols:csscontrol>
Назад:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Linq;
using TTC.iTropics.Utilities;
namespace WebApplication1
{
[DefaultProperty("Stylesheets")]
[ParseChildren(true, "Stylesheets")]
public class CssControl : LiteralControl
{
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<Stylesheet> Stylesheets { get; set; }
public CssControl()
{
Stylesheets = new List<Stylesheet>();
}
protected override void Render(HtmlTextWriter output)
{
if (HttpContext.Current.IsDebuggingEnabled)
{
const string format = "<link rel=\"Stylesheet\" href=\"stylesheets/{0}\"></link>";
foreach (Stylesheet sheet in Stylesheets)
output.Write(format, sheet.File);
}
else
{
const string format = "<link type=\"text/css\" rel=\"Stylesheet\" href=\"stylesheets/CssHandler.ashx?cssfiles={0}&version={1}\"/>";
IEnumerable<string> stylesheetsArray = Stylesheets.Select(s => s.File);
string stylesheets = String.Join(",", stylesheetsArray.ToArray());
string version = "1.00" //your version number
output.Write(format, stylesheets, version);
}
}
}
public class Stylesheet
{
public string File { get; set; }
}
}
HttpContext.Current.IsDebuggingEnabled подключен к следующему параметру в вашем файле web.config:
<system.web>
<compilation debug="false">
</system.web>
Итак, если ваш сайт находится в режиме отладки, вы получаете HTML-разметку, например:
<link rel="Stylesheet" href="stylesheets/formatting.css"></link>
<link rel="Stylesheet" href="stylesheets/layout.css"></link
<link rel="Stylesheet" href="stylesheets/main.css"></link>
Но если вы находитесь в производственном режиме (debug = false), вы получите разметку, подобную этой:
<link type="text/css" rel="Stylesheet" href="CssHandler.ashx?cssfiles=main.css,layout.css,formatting.css&version=1.0"/>
Последний затем явно вызовет CSSHandler, который позаботится о комбинировании, минимизации и чтении в кэш-памяти вашего статического CSS-контента.
Все вышеперечисленное также может быть продублировано для статического контента JavaScript:
`JSHandler.ashx:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
namespace WebApplication1
{
public class JSHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
string[] jsFiles = context.Request.QueryString["jsfiles"].Split(',');
List<string> files = new List<string>();
StringBuilder response = new StringBuilder();
foreach (string jsFile in jsFiles)
{
if (!jsFile.EndsWith(".js", StringComparison.OrdinalIgnoreCase))
{
//log custom exception
context.Response.StatusCode = 403;
return;
}
try
{
string filePath = context.Server.MapPath(jsFile);
files.Add(filePath);
string js = File.ReadAllText(filePath);
string compressedJS = Yahoo.Yui.Compressor.JavaScriptCompressor.Compress(js);
response.Append(compressedJS);
}
catch (Exception ex)
{
//log exception
context.Response.StatusCode = 500;
return;
}
}
context.Response.Write(response.ToString());
string version = "1.0"; //your dynamic version number here
context.Response.ContentType = "application/javascript";
context.Response.AddFileDependencies(files.ToArray());
HttpCachePolicy cache = context.Response.Cache;
cache.SetCacheability(HttpCacheability.Public);
cache.VaryByParams["jsfiles"] = true;
cache.VaryByParams["version"] = true;
cache.SetETag(version);
cache.SetLastModifiedFromFileDependencies();
cache.SetMaxAge(TimeSpan.FromDays(14));
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
}
}
и сопровождающий его АОонтроль:
Передняя панель:
<customcontrols:JSControl ID="jsControl" runat="server">
<customcontrols:Script File="jquery/jquery-1.3.2.js" />
<customcontrols:Script File="main.js" />
<customcontrols:Script File="creditcardpayments.js" />
</customcontrols:JSControl>
Назад:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Linq;
namespace WebApplication1
{
[DefaultProperty("Scripts")]
[ParseChildren(true, "Scripts")]
public class JSControl : LiteralControl
{
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<Script> Scripts { get; set; }
public JSControl()
{
Scripts = new List<Script>();
}
protected override void Render(HtmlTextWriter writer)
{
if (HttpContext.Current.IsDebuggingEnabled)
{
const string format = "<script src=\"scripts\\{0}\"></script>";
foreach (Script script in Scripts)
writer.Write(format, script.File);
}
else
{
IEnumerable<string> scriptsArray = Scripts.Select(s => s.File);
string scripts = String.Join(",", scriptsArray.ToArray());
string version = "1.0" //your dynamic version number
const string format = "<script src=\"scripts/JsHandler.ashx?jsfiles={0}&version={1}\"></script>";
writer.Write(format, scripts, version);
}
}
}
public class Script
{
public string File { get; set; }
}
}
Включение GZIP:
Как говорит Джефф Этвуд, включение Gzip на сервере вашего веб-сайта не составляет труда. После некоторой трассировки я решил включить Gzip для файлов следующих типов:
.css
.js
.axd (файлы Microsoft Javascript)
.aspx (обычное содержимое веб-форм ASP.NET)
.ashx (Наши обработчики)
Чтобы включить сжатие HTTP на веб-сервере IIS 6.0:
Откройте IIS, щелкните правой кнопкой мыши Веб-сайты, вкладку «Службы», включите «Сжатие файлов приложения» и «Сжатие статических файлов».
Стоп IIS
Откройте метабазу IIS в блокноте (C: \ WINDOWS \ system32 \ inetsrv \ MetaBase.xml) и сделайте резервную копию, если вы нервничаете по поводу этих вещей
Найдите и перезапишите два элемента IIsCompressionScheme и один элемент IIsCompressionSchemes следующим образом:
И это все! Это сохраненомы тратим кучу пропускной способности и привели к более гибкому веб-приложению.
Наслаждайтесь!