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); } } }