Простите за плохой английский, но думаю, это не проблема. Я просто не хочу делиться хорошим вспомогательным классом, который я сделал для объединения , минимизации и gzip наших скриптов и стилей с помощью Microsoft Ajax Minifier .Перед началом загрузите ICSharpCode.SharpZipLib . Это библиотека с открытым исходным кодом для использования gzip .
Начнем с web.config (я сосредоточусь на IIS7). Здесь мы говорим нашему приложению, что любой запрос к расширению cssh или jsh будет перенаправлен в класс MinifierHelper . Я решил использовать эти расширения ( cssh и jsh ), чтобы, если мы по какой-либо причине не хотим минимизировать конкретный скрипт или стиль, используйте его так, как вы обычно используете.
Я использую папки Scripts и Styles для хранения файлов. Я не использую содержимое папки, как это предлагает Visual Studio.
Следующим шагом является настройка global.asax. Мы должны указать нашему приложению, чтобы оно не маршрутизировало эти папки. Добавьте эти строки в свой метод RegisterRoutes .
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("Scripts/{*path}");
routes.IgnoreRoute("Styles/{*path}");
...
}
Хорошо. Теперь я покажу, как использовать наш класс. В представлении:
В моем примере я сделал логику для всех моих скриптов и стилей внутри папок внутри скриптов / стилей. Пример: Сайт -> Скрипты -> Домашняя страница -> index.css . Я использую ту же структуру, что и для представлений скриптов и стилей. Пример: Сайт -> Просмотры -> Главная -> index.cshtml . Вы можете изменить этот узор, если хотите.
Теперь код для создания волшебства:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Web.SessionState;
using ICSharpCode.SharpZipLib.GZip;
using Microsoft.Ajax.Utilities;
namespace Site.Helpers
{
public abstract class MinifierBase : IHttpHandler, IRequiresSessionState
{
#region Fields
private HttpContext context;
private byte[] responseBytes;
private bool isScript;
protected string fileName;
protected string folderName;
protected List files;
#endregion
#region Properties
public bool IsReusable
{
get { return false; }
}
#endregion
#region Methods
public static string setUrl(string url)
{
var publishDate = ConfigurationManager.AppSettings["PublishDate"];
return url + "h" + ((publishDate != null) ? "?id=" + publishDate : "");
}
public void ProcessRequest(HttpContext context)
{
this.context = context;
this.isScript = context.Request.Url.PathAndQuery.Contains("/Scripts/");
this.process();
}
private void process()
{
if (this.context.Request.QueryString.HasKeys())
{
string url = this.context.Request.Url.PathAndQuery;
if (this.context.Cache[url] != null)
{
this.responseBytes = this.context.Cache[url] as byte[];
}
else
{
this.writeResponseBytes();
this.context.Cache.Add
(
url,
this.responseBytes,
null,
DateTime.Now.AddMonths(1),
Cache.NoSlidingExpiration,
CacheItemPriority.Low,
null
);
}
}
else
{
this.writeResponseBytes();
}
this.writeBytes();
}
private void writeResponseBytes()
{
using (MemoryStream ms = new MemoryStream(8092))
{
using (Stream writer = this.canGZip() ? (Stream)(new GZipOutputStream(ms)) : ms)
{
var sb = new StringBuilder();
var regex = new Regex(@"^/.+/(?.+)/(?.+)\..+");
var url = regex.Match(this.context.Request.Path);
var folderName = url.Groups["folder"].Value;
var fileName = url.Groups["name"].Value;
this.getFileNames(fileName, folderName).ForEach(delegate(string file)
{
sb.Append(File.ReadAllText(this.context.Server.MapPath(file)));
});
var minifier = new Minifier();
var minified = string.Empty;
if (this.isScript)
{
var settings = new CodeSettings();
settings.LocalRenaming = LocalRenaming.CrunchAll;
settings.OutputMode = OutputMode.SingleLine;
settings.PreserveImportantComments = false;
settings.TermSemicolons = true;
minified = minifier.MinifyJavaScript(sb.ToString(), settings);
}
else
{
var settings = new CssSettings();
settings.CommentMode = CssComment.Important;
settings.OutputMode = OutputMode.SingleLine;
minified = minifier.MinifyStyleSheet(sb.ToString(), settings);
}
var bts = Encoding.UTF8.GetBytes(minified);
writer.Write(bts, 0, bts.Length);
}
this.responseBytes = ms.ToArray();
}
}
private List getFileNames(string fileName, string folderName = "")
{
this.files = new List();
this.fileName = fileName;
this.folderName = folderName;
if (folderName == "Global" && fileName == "global-min")
{
if (this.isScript) this.addGlobalScripts();
else this.addDefaultStyles();
}
else
{
var flags = BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance;
var mi = this.GetType().GetMethod
(
"add" +
this.folderName +
CultureInfo.CurrentCulture.TextInfo.ToTitleCase(fileName).Replace("-", "") +
(this.isScript ? "Scripts" : "Styles"),
flags
);
if (mi != null)
{
mi.Invoke(this, null);
}
else
{
if (this.isScript) this.addDefaultScripts();
else this.addDefaultStyles();
}
}
return files;
}
private void writeBytes()
{
var response = this.context.Response;
response.AppendHeader("Content-Length", this.responseBytes.Length.ToString());
response.ContentType = this.isScript ? "text/javascript" : "text/css";
if (this.canGZip())
{
response.AppendHeader("Content-Encoding", "gzip");
}
else
{
response.AppendHeader("Content-Encoding", "utf-8");
}
response.ContentEncoding = Encoding.Unicode;
response.OutputStream.Write(this.responseBytes, 0, this.responseBytes.Length);
response.Flush();
}
private bool canGZip()
{
string acceptEncoding = this.context.Request.Headers["Accept-Encoding"];
return (!string.IsNullOrEmpty(acceptEncoding) && (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate")));
}
protected abstract void addGlobalScripts();
protected abstract void addGlobalStyles();
protected abstract void addDefaultScripts();
protected abstract void addDefaultStyles();
#endregion
}
Это базовый класс. Теперь мы создадим класс Helper, унаследованный от базового класса. Это класс, в котором мы выбираем, какие скрипты следует добавить.
public class MinifierHelper : MinifierBase
{
#region Methods
Чтобы объединить и минимизировать глобальные скрипты / стили, добавьте текущую строку в ваше представление:
Она вызовет методы addGlobalScripts / addGlobalStyles в нашем классе MinifierHelper .
protected override void addGlobalScripts()
{
this.files.Add("~/Scripts/Lib/jquery-1.6.2.js");
this.files.Add("~/Scripts/Lib/jquery-ui-1.8.16.js");
this.files.Add("~/Scripts/Lib/jquery.unobtrusive-ajax.js");
this.files.Add("~/Scripts/Lib/jquery.validate.js");
...
}
protected override void addGlobalStyles()
{
this.files.Add("~/Styles/Global/reset.css");
this.files.Add("~/Styles/Global/main.css");
this.files.Add("~/Styles/Global/form.css");
...
}
Чтобы минимизировать определенный скрипт / стиль (специфичный для страницы), добавьте текущую строку в ваше представление:
Класс MinifierHelper попытается найти метод с именем "add" + FolderName + FileName + " Стили ». В нашем случае он будет искать addCurriculumIndexStyles . В моем примере он существует, поэтому метод будет запущен.
public void addCurriculumIndexStyles()
{
this.files.Add("~/Styles/Global/curriculum.css");
this.files.Add("~/Styles/Curriculum/personal-info.css");
this.files.Add("~/Styles/Curriculum/academic-info.css");
this.files.Add("~/Styles/Curriculum/professional-info.css");
}
Если класс не находит этот конкретный метод, он запускает метод по умолчанию. Метод по умолчанию минимизирует сценарий / стиль, используя ту же папку / указанное имя.
protected override void addDefaultScripts()
{
this.files.Add("~/Scripts/" + this.folderName + "/" + this.fileName + ".js");
}
protected override void addDefaultStyles()
{
this.files.Add("~/Styles/" + this.folderName + "/" + this.fileName + ".css");
}
Не забудьте закрыть регион и класс.
#endregion
}
Вот и все. Надеюсь, вы поняли.
Я забыл сказать одну вещь. В файле web.config добавьте в AppSettings ключ с именем PublishDate. Я ввел в значение строку с полной датой и временем (например: 261020111245). Цель - быть уникальным. Этот ключ будет использоваться для кеширования наших миниатюрных скриптов. Если вы не создадите этот ключ, ваше приложение не будет использовать кеш. Я рекомендую использовать это. Поэтому каждый раз, когда вы обновляете свои скрипты / стили, обновляйте также и свой PublishDate.