Web应用程序中包含大量的样式(css)和脚本(js)文件,这些文件的引用、管理和发布有不少解决方案。在Asp.Net MVC应用程序中,你们最熟悉的解决方案应属Microsoft.AspNet.Web.Optimization这个package。这个package的使用也挺方便,对我来讲,它依赖太多package,这点不合我胃口,我是比较崇尚精简的那种。接下来介绍这个package的使用及如何将它完美的替换。css
将要合并的文件添加到BundleTable.Bundles集合中便可,样式文件使用StyleBundle类,脚本文件使用ScriptBundle类。示例以下:html
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { var style = new StyleBundle("~/Content/login") .Include("~/Content/common.css", "~/Content/login.css"); bundles.Add(style); var script = new ScriptBundle("~/Scripts/login") .Include("~/Scripts/common.js", "~/Scripts/login.js"); bundles.Add(script); } }
View页面使用Styles和Scripts两个类来呈现。示例以下:jquery
@Styles.Render("~/Content/login") @Scripts.Render("~/Scripts/login")
这里只简单介绍一下Bundle的使用。我的以为主要有以下问题:web
为了完美替换Microsoft.AspNet.Web.Optimization的Bundle,我采用了Bundler & Minifier这个VS的扩展,它能够方便的配置和生成样式及脚本的min文件。这个扩展只能生成min文件,而没有Bundle那样能够根据开发环境和生产环境来输出对应的源文件和min文件,不过这个问题很好解决,下面来介绍如何实现。json
[ { "outputFileName": "static/modules/login/index.min.css", "inputFiles": [ "static/modules/login/index.css" ] }, { "outputFileName": "static/modules/login/index.min.js", "inputFiles": [ "static/libs/jquery.min.js", "static/libs/jquery.md5.js", "static/modules/core/js", "static/modules/login/index.js" ] } ]
<configuration> <system.web> <compilation debug="true" /> </system.web> </configuration>
根据这个节点,咱们来实现不一样环境下样式和脚本文件的输出,即开发时输出源文件,生产环境下输出min文件。咱们添加HtmlHelper类的扩展方法,一个是MinStyle输出样式,一个是MinScript输出脚本。View页面使用以下:c#
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> @Html.MinStyle("static/modules/login/index.min.css") </head> <body> <div class="login"> ... </div> @Html.MinScript("static/modules/login/index.min.js") </body> </html>
下面是这两个扩展方法的具体实现:dom
public static class HtmlExtension { public static IHtmlString MinStyle(this HtmlHelper helper, string path) { var format = "<link rel=\"stylesheet\" href=\"/{0}\">"; var html = GetHtmlString(helper, format, path); return new HtmlString(html); } public static IHtmlString MinScript(this HtmlHelper helper, string path) { var format = "<script src=\"/{0}\"></script>"; var html = GetHtmlString(helper, format, path); return new HtmlString(html); } private static string GetHtmlString(HtmlHelper helper, string format, string path) { var random = DateTime.Now.ToString("yyMMddss"); var html = string.Format(format, $"{path}?r={random}"); var httpContext = helper.ViewContext.RequestContext.HttpContext; if (httpContext.IsDebuggingEnabled) { var bundle = BundleInfo.GetBundle(httpContext, path); if (bundle != null && bundle.HasInputFiles) { var rootPath = httpContext.Server.MapPath("~/"); var paths = new List<string>(); foreach (var inputFile in bundle.inputFiles) { var inputPath = rootPath + inputFile; if (File.Exists(inputPath)) { paths.Add(string.Format(format, $"{inputFile}?r={random}")); } else if (Directory.Exists(inputPath)) { var files = Directory.GetFiles(inputPath); foreach (var file in files) { var filePath = file.Replace(rootPath, "").Replace("\\", "/"); paths.Add(string.Format(format, $"{filePath}?r={random}")); } } } html = string.Join(Environment.NewLine, paths); } } return html; } class BundleInfo { public string outputFileName { get; set; } public List<string> inputFiles { get; set; } public bool HasInputFiles { get { return inputFiles != null && inputFiles.Count > 0; } } public static BundleInfo GetBundle(HttpContextBase httpContext, string outputFile) { var jsonFile = httpContext.Server.MapPath("~/bundleconfig.json"); if (!File.Exists(jsonFile)) return null; var json = File.ReadAllText(jsonFile); if (string.IsNullOrWhiteSpace(json)) return null; var bundles = json.FromJson<List<BundleInfo>>(); if (bundles == null || bundles.Count == 0) return null; return bundles.FirstOrDefault(b => b.outputFileName == outputFile); } } }