使用 Razor 在 Asp.Net MVC3 上组合、缩小和 GZIP 样式和脚本的完整解决方案
抱歉我的英语不好,但我想这不会有问题。我只是不想分享一个很好的助手类,我使用 组合、缩小和gzip我们的脚本和样式="http://ajaxmin.codeplex.com/" rel="noreferrer">Microsoft Ajax Minifier。开始之前,请下载 ICSharpCode.SharpZipLib。这是一个使用gzip的开源库。
让我们从 web.config 开始(我将重点关注 IIS7)。在这里,我们对应用程序说,对 cssh 或 jsh 扩展发出的任何请求都将转发到 MinifierHelper 类。我选择使用这些扩展(cssh 和 jsh),以便如果我们出于任何原因想要不缩小特定的脚本或样式,就按照您使用的方式使用它通常情况下。
<system.webServer>
<handlers>
<remove name="ScriptHandler" />
<remove name="StyleHandler" />
<add name="ScriptHandler" verb="*" path="*.jsh" type="Site.Helpers.MinifierHelper" resourceType="Unspecified" />
<add name="StyleHandler" verb="*" path="*.cssh" type="Site.Helpers.MinifierHelper" resourceType="Unspecified" />
</handlers>
</system.webServer>
我使用文件夹 Scripts 和 Styles 来存储文件。我没有按照 Visual Studio 的建议使用 Content 文件夹。
下一步是配置 global.asax。我们必须告诉我们的应用程序不要路由这些文件夹。将这些行添加到您的 RegisterRoutes 方法中。
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("Scripts/{*path}");
routes.IgnoreRoute("Styles/{*path}");
...
}
好的。现在我将展示如何使用我们的类。在视图中:
<link href="/Styles/Folder/File.cssh" type="text/css" rel="stylesheet" />
<script src="/Scripts/Folder/File.jsh" type="text/javascript"></script>
在我的示例中,我将所有脚本和样式的逻辑都放在脚本/样式内的文件夹内。 例如:站点 ->脚本->首页->索引.css。我使用与脚本和样式视图相同的结构。 例如:站点 ->浏览次数->首页->索引.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<string> 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(@"^/.+/(?<folder>.+)/(?<name>.+)\..+");
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<String> getFileNames(string fileName, string folderName = "")
{
this.files = new List<String>();
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
要组合和缩小全局脚本/样式,请将当前行添加到视图中:
<link href="@MinifierHelper.setUrl("/Styles/Global/global-min.css")" type="text/css" rel="stylesheet" />
<script src="@MinifierHelper.setUrl("/Scripts/Global/global-min.js")" type="text/javascript"></script>
它将调用 MinifierHelper 类中的 addGlobalScripts/addGlobalStyles 方法。
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");
...
}
要缩小特定脚本/样式(特定于页面),请将当前行添加到您的视图中:
<link href="@MinifierHelper.setUrl("/Styles/Curriculum/index.css")" type="text/css" rel="stylesheet" />
MinifierHelper 类将尝试查找名为 "add" + FolderName + FileName + "Styles" 的方法。在我们的例子中,它将查找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)。目标是独一无二。该密钥将用于缓存我们的缩小脚本。如果您不创建此密钥,您的应用程序将不会使用缓存。我建议使用这个。因此,每次更新脚本/样式时,也要更新您的发布日期。
<add key="PublishDate" value="261020111245" />
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Mindscape Web Workbench 是完成您正在寻找的项目的绝佳工具。
http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a
这是一篇关于它的很好的博客文章:
http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97 -8008f9db141a
The Mindscape Web Workbench is a great tool for doing the items you are looking for.
http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a
Here is a good blog post about it:
http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a