Web應用程式中包含大量的樣式(css)和腳本(js)文件,這些文件的引用、管理和發佈有很多解決方案。在Asp.Net MVC應用程式中,大家最熟悉的解決方案應屬Microsoft.AspNet.Web.Optimization這個package。這個package的使用也挺方便,對我來說,它依賴太 ...
Web應用程式中包含大量的樣式(css)和腳本(js)文件,這些文件的引用、管理和發佈有很多解決方案。在Asp.Net MVC應用程式中,大家最熟悉的解決方案應屬Microsoft.AspNet.Web.Optimization這個package。這個package的使用也挺方便,對我來說,它依賴太多package,這點不合我胃口,我是比較崇尚精簡的那種。接下來介紹這個package的使用及如何將它完美的替換。
1. Microsoft.AspNet.Web.Optimization的Bundle使用
將要合併的文件添加到BundleTable.Bundles集合中即可,樣式文件使用StyleBundle類,腳本文件使用ScriptBundle類。示例如下:
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(style);
}
}
View頁面使用Styles和Scripts兩個類來呈現。示例如下:
@Styles.Render("~/Content/login")
@Scripts.Render("~/Scripts/login")
這裡只簡單介紹一下Bundle的使用。個人覺得主要有如下問題:
- 依賴過多的package,有WebGrease、Antlr、Newtonsoft.Json;
- 不同文件夾的樣式文件不能同時輸出一個min文件,若包在一起時,有些樣式文件引用的圖片無法顯示,這個問題我沒想去解決,有了上面那一條,也不想去解決它。
2. 完美的替換方案
為了完美替換Microsoft.AspNet.Web.Optimization的Bundle,我採用了Bundler & Minifier這個VS的擴展,它可以方便的配置和生成樣式及腳本的min文件。這個擴展只能生成min文件,而沒有Bundle那樣可以根據開發環境和生產環境來輸出對應的源文件和min文件,不過這個問題很好解決,下麵來介紹如何實現。
- 安裝Bundler & Minifier擴展及配置
在VS中點擊“工具-擴展和更新-聯機”,再輸入Bundler搜索,下載,重啟VS完成安裝。 - Bundle的配置
它的配置很簡單,配一個outputFileName和inputFiles集合即可。打開bundleconfig.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/common.js",
"static/modules/login/index.js"
]
}
]
- 解決開發環境和生產環境輸出特性
我們知道Web.config文件有如下節點,可以設置當前程式的環境,可以通過HttpContextBase類的IsDebuggingEnabled屬性來獲取。
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
</configuration>
根據這個節點,我們來實現不同環境下樣式和腳本文件的輸出,即開發時輸出源文件,生產環境下輸出min文件。我們添加HtmlHelper類的擴展方法,一個是MinStyle輸出樣式,一個是MinScript輸出腳本。View頁面使用如下:
<!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>
下麵是這兩個擴展方法的具體實現:
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)
{
var paths = bundle.inputFiles.Select(f => string.Format(format, f + "?r=" + random));
html = string.Join(Environment.NewLine, paths);
}
}
return html;
}
class BundleInfo
{
public string outputFileName { get; set; }
public List<string> inputFiles { get; set; }
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);
}
}
}