Microsoft.AspNet.Web.Optimization.Bundle的完美替換方案

Web應用程序中包含大量的樣式(css)和腳本(js)文件,這些文件的引用、管理和發佈有不少解決方案。在Asp.Net MVC應用程序中,你們最熟悉的解決方案應屬Microsoft.AspNet.Web.Optimization這個package。這個package的使用也挺方便,對我來講,它依賴太多package,這點不合我胃口,我是比較崇尚精簡的那種。接下來介紹這個package的使用及如何將它完美的替換。css

1. Microsoft.AspNet.Web.Optimization的Bundle使用

將要合併的文件添加到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

  • 依賴過多的package,有WebGrease、Antlr、Newtonsoft.Json;
  • 不一樣文件夾的樣式文件不能同時輸出一個min文件,若包在一塊兒時,有些樣式文件引用的圖片沒法顯示,這個問題我沒想去解決,有了上面那一條,也不想去解決它。

2. 完美的替換方案

爲了完美替換Microsoft.AspNet.Web.Optimization的Bundle,我採用了Bundler & Minifier這個VS的擴展,它能夠方便的配置和生成樣式及腳本的min文件。這個擴展只能生成min文件,而沒有Bundle那樣能夠根據開發環境和生產環境來輸出對應的源文件和min文件,不過這個問題很好解決,下面來介紹如何實現。json

  • 安裝Bundler & Minifier擴展及配置
    在VS中點擊「工具-擴展和更新-聯機」,再輸入Bundler搜索,下載,重啓VS完成安裝。
  • Bundle的配置
    它的配置很簡單,配一個outputFileName和inputFiles集合便可,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/core/js",
      "static/modules/login/index.js"
    ]
  }
]
  • 解決開發環境和生產環境輸出特性
    咱們知道Web.config文件有以下節點,能夠設置當前程序的環境,能夠經過HttpContextBase類的IsDebuggingEnabled屬性來獲取。
<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);
        }
    }
}
相關文章
相關標籤/搜索