欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-04 11:10:17
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);
        }
    }
}