zoukankan      html  css  js  c++  java
  • 打包和压缩(Bundle and Minification)(译文)

     打包和压缩使你可以在ASP.NET 4.5中用来改善请求加载时间的两项技术。打包和压缩通过减少向服务器发送请求的次数以及减少请求资源的大小(例如CSS和JavaScript)来改善加载时间。

      当前大多数主流的浏览器将每个同名主机的并发连接数限制为6。这就意味着当有6个请求正在被处理时,浏览器向主机发送的额外请求将会被加载到队列。在下图中,IE F12开发者工具中网络选项卡显示了向示例应用程序中请求About视图中资源的所需时间。

      灰色进度条显示了浏览器在6个连接限制的情况下请求所需的排队时间(开始:从最初创建请求到发送请求之间的时间)。黄色进度条是第一个字节所到达的请求时间,即发送请求和接收来自服务器的第一个响应的时间(请求:接收到第一个字节所需的时间。发送请求并接收服务器的第一个响应所需花费的时间)。蓝色进度条显示了接收服务器响应数据所需要的时间(响应:接收服务器的响应所花费的时间)。你可以双击一个资源查看详细的时间信息。例如,下图中显示了加载 /Scripts/MyScripts/JavaScript6.js 文件的时间详情。

      上面的图片显示了开始事件,给出了由于浏览器限制连接数量请求所需的排队时间。在这种情况下,请求被排队46毫秒等待另一个请求完成。  

      打包(Bundling)

      打包(Bundling)是ASP.NET 4.5的一个新特性,它使得将多个文件合并成一个文件变得更容易。你可以创建CSS,JavaScript和其他包。更少的文件意味着更少的HTTP请求,这样可以改善第一页的加载性能。

      下面的图片显示了之前显示的关于About视图的相同时序图,但是这一次启用了打包和压缩。

      压缩(Minification)

      压缩是对脚本或CSS执行各种不同的代码优化,比如删除不必要的空白和注释,并将变量名缩短为一个字符,观察下面的JavaScript函数。

    AddAltToImg = function (imageTagAndImageID, imageContext) {
        ///<signature>
        ///<summary> Adds an alt tab to the image
        // </summary>
        //<param name="imgElement" type="String">The image selector.</param>
        //<param name="ContextForImage" type="String">The image context.</param>
        ///</signature>
        var imageElement = $(imageTagAndImageID, imageContext);
        imageElement.attr('alt', imageElement.attr('id').replace(/ID/, ''));
    }

      压缩后,该函数被简化为:

    AddAltToImg = function (n, t) { var i = $(n, t); i.attr("alt", i.attr("id").replace(/ID/, "")) }

      除了删除注释和不必要的空白之外,一下参数和变量名被重新命名(缩短)如下:

    Original         Renamed            
    imageTagAndImageID n
    imageContext t
    imageElement i

      打包和压缩的影响(Impact Of Bundling and Minification)

      下表列出了在示例程序中单独请求资源和使用打包和压缩(B/M)后请求资源之间的几个重要区别。

      Using B/M Without B/M Change
    File Requests 9 34 256%
    KB Sent 3.26 11.92 266%
    KB Received 388.51 530 36%
    Load Time 510 MS 780 MS 53%

      因为之前浏览器发送请求时的HTTP头文件的非常冗长,使用了打包(bundle)之后发送的字节明显减少了(The bytes sent had a significant reduction with bundling as browsers are fairly verbose with the HTTP headers they apply on requests.此句感觉翻译的不通顺,原句在此,请博友帮忙译一下,谢谢!)。由于大的文件(Scripts/jquery-ui-1.8.11.min.js和Scripts/jquery-1.7.1.min.js)已经被压缩过了(已经是.min文件了)所以所接收的字节减少并没有那么大。注意:示例程序中的计时是使用了Fiddler工具来模拟慢速网络。(从Fiddler Role 菜单中,选择Performance然后选择Simulate Modem Speeds。)

      调试打包和压缩的JavaScript(Debugging Bundled and Minified JavaScript)

      在开发环境(Web.config文件中的compilation 元素要设置debug="true")中调试是非常容易的,因为JavaScript文件没有被打包和压缩过。你也可以调试一个发布版本,在这个版本中,你的JavaScript文件是打包和压缩过的。使用IE F12开发者工具,你可以使用下面的方法调试一个包含在压缩包中的JavaScript函数:

      1,选择脚本(Scrip)选项卡,然后选择开始调试(Start debugging)按钮。

      2,使用assets按钮选择包含要调试的JavaScript函数的包。

      3,通过选择配置按钮(Configuration button),然后选择格式JavaScript(Format JavaScript),来格式化压缩的JavaScript。

      4,在搜索脚本(Search Scrip)输入框中,搜索你想要调试的函数名。在下图中,搜索脚本(Search Scrip)输入框中键入的是AddAltToImg

      使用F12开发者工具调试的更多信息,请参考MSDN文章 使用F12开发者工具调试JavaScript错误

      控制打包和压缩(Controlling Bundling and Minification)

      可以通过在Web.config文件的compilation 元素中设置debug属性的值来禁用或启用打包和压缩功能。在下面的XML文件中,debug被设置为“true”来禁用打包和压缩。

    <system.web>
        <compilation debug="true" />
        <!-- Lines removed for clarity. -->
    </system.web>

      若要启用打包和压缩,则设置debug的值为“false”。你可以用BundleTable类中的EnableOptimizations属性来覆盖Web.config中的设置。下面的代码启用了打包和压缩并覆盖了在Web.config文件中的任何设置。

    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                     "~/Scripts/jquery-{version}.js"));
    
        // Code removed for clarity.
        BundleTable.EnableOptimizations = true;
    }

      注意:除非EnableOptimizationstrue,或者Web.config文件中compilation 元素的debug属性设置为false,文件才会被打包或压缩。此外,.min版本的文件不会被使用,完整的debug版本将会被选种。EnableOptimizations会覆盖Web.config文件中compilation 元素的debug属性。

      在ASP.NET Web Forms和Web Pages中使用打包和压缩(Using Bundling and Minification with ASP.NET Web Forms and Web Pages)

      ASP.NET MVC中使用打包和压缩(Using Bundling and Minification with ASP.NET MVC)

      在本节中,我们将创建一个ASP.NET MVC项目研究打包和压缩。首先,创建一个新的ASP.NET MVC Internet项目,命名为MvcBM,无需更改任何缺省值。

      打开App_StartBundleConfig.cs文件查看RegisterBundles方法,它被用来创建,注册和配置bundles。下面的代码展示了RegisterBundles方法的一部分。

    public static void RegisterBundles(BundleCollection bundles)
    {
         bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                     "~/Scripts/jquery-{version}.js"));
             // Code removed for clarity.
    }

      前面的代码创建了一个名为~/bundle/jquery的新JavaScript包,它包含了与通配符字符串“~/Script/jquery-{version}.js”匹配的JavaScript文件夹中的所有合适的文件(即debug或压缩(minified)而非.vsdoc)文件。在ASP.NET MVC 4中,这意味着可以是一个调试文件,jquery-1.7.1.js文件将会被加载到bundle包中。在发布配置中,jquery-1.7.1.min.js将会被加到Bundle中。Bundling框架遵循以下几个常见的约定:

    • 发布版本中,当“FileX.min.js”和“FileX.js”都存在时,“.min”文件将会被选中。
    • 调试版本中,不会选择“.min”版本。
    • 忽略“-vsdoc”文件(例如jquery-1.7.1-vsdoc.js),它常被用作智能提示。

      上面显示的{version}通配符用于自动创建一个Jquery包,并在JavaScript文件夹中使用适当的jquery版本。在本例中,使用通配符有以下好处:

    • 允许你用NuGet更新到新的jQuery版本,而不需要改变前面的bundling代码或你的视图页面中的jQuery引用。
    • debug配置时自动选择完整版本,发布版本自动选择“.min”版本。

      用CDN(Using a CDN)

      下面的代码用一个CDN jQuery包替换本地的jQuery包。

    public static void RegisterBundles(BundleCollection bundles)
    {
        //bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
        //            "~/Scripts/jquery-{version}.js"));
    
        bundles.UseCdn = true;   //enable CDN support
    
        //add link to jquery on the CDN
        var jqueryCdnPath = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";
    
        bundles.Add(new ScriptBundle("~/bundles/jquery",
                    jqueryCdnPath).Include(
                    "~/Scripts/jquery-{version}.js"));
    
        // Code removed for clarity.
    }

      在上面的代码中,当在发布模式下jQuery将从CDN中请求,而在调试模式下,jQuery的调试版本将从本地获取。在使用CDN时,如果CDN请求失败,应该有一个备用机制。在layout文件末尾下面的标记片段显示了在CDN请求jQuery失败时的处理脚本。

    </footer>
    
            @Scripts.Render("~/bundles/jquery")
    
            <script type="text/javascript">
                if (typeof jQuery == 'undefined') {
                    var e = document.createElement('script');
                    e.src = '@Url.Content("~/Scripts/jquery-1.7.1.js")';
                    e.type = 'text/javascript';
                    document.getElementsByTagName("head")[0].appendChild(e);
    
                }
            </script> 
    
            @RenderSection("scripts", required: false)
        </body>
    </html>

       创建一个Bundle(Create a Bundle)

      Bundle类的Include方法参数是一个字符串数组,每个字符串都是一个资源的虚拟路径。下面的代码来自于App_StartBundleConfig.cs文件的RegisterBundles方法,展示了如何向一个bundle添加多个文件:

    bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
        "~/Content/themes/base/jquery.ui.core.css",
        "~/Content/themes/base/jquery.ui.resizable.css",
        "~/Content/themes/base/jquery.ui.selectable.css",
        "~/Content/themes/base/jquery.ui.accordion.css",
        "~/Content/themes/base/jquery.ui.autocomplete.css",
        "~/Content/themes/base/jquery.ui.button.css",
        "~/Content/themes/base/jquery.ui.dialog.css",
        "~/Content/themes/base/jquery.ui.slider.css",
        "~/Content/themes/base/jquery.ui.tabs.css",
        "~/Content/themes/base/jquery.ui.datepicker.css",
        "~/Content/themes/base/jquery.ui.progressbar.css",
        "~/Content/themes/base/jquery.ui.theme.css"));

      Bundle类的IncludeDirectory方法用于包含目录中(以及可选的所有子目录)与搜索模式匹配的所有文件。下面显示了Bundle类的IncludeDirectory方法API:

    public Bundle IncludeDirectory(
        string directoryVirtualPath,  // The Virtual Path for the directory.
        string searchPattern)         // The search pattern.
    
    public Bundle IncludeDirectory(
        string directoryVirtualPath,  // The Virtual Path for the directory.
        string searchPattern,         // The search pattern.
        bool searchSubdirectories)    // true to search subdirectories.

      打包的文件使用Render方法在视图中引用,(Styles.Render用于CSS,Script.Render用于JavaScript)。下面的标记是ViewsShared\_Layout.cshtml文件中的,展示了默认的ASP.NET internet项目视图如何引用打包的CSS和JavaScript。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        @* Markup removed for clarity.*@    
        @Styles.Render("~/Content/themes/base/css", "~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        @* Markup removed for clarity.*@
       
       @Scripts.Render("~/bundles/jquery")
       @RenderSection("scripts", required: false)
    </body>
    </html>

      注意Render方法采用了字符串数组作为参数,因此你可以在一行代码上添加多个包。通常你会需要使用Render方法来创建必要的HTML来引用该资源。你也可以使用URL方法使该URL定位到资源而不需要用标记来引用资源。假设你想使用新的HTML5 async特性。下面的代码展示如何使用URL方法引用modernizr。

    <head>
        @*Markup removed for clarity*@
        <meta charset="utf-8" />
        <title>@ViewBag.Title - MVC 4 B/M</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
    
       @* @Scripts.Render("~/bundles/modernizr")*@
    
        <script src='@Scripts.Url("~/bundles/modernizr")' async> </script>
    </head>

      使用“*”通配符选择文件(Using the "*" Wildcard Character to Select Files)

      在Include方法中指定的虚拟路径和IncludeDirectory方法中的搜索模式都可以在最后一个路径段中接收一个“*”通配符作为前缀或后缀。搜索字符串不区分大小写。IncludeDirectory方法有搜索子目录的选项。

      思考一个具有以下JavaScript文件的项目:

    • ScriptCommonAddAltToImg.js
    • ScriptCommonToggleDiv.js
    • ScriptCommonToggleImg.js
    • ScriptCommonSub1ToggleLinks.js

      下表显示了使用通配符添加到包中的文件:

    Call Files Added or Exception Raised
    Include("~/Scripts/Common/*.js") AddAltToImg.js,ToggleDiv.js,ToggleImg.js
    Include("~/Scripts/Common/T*.js") 无效的异常模式。通配符只允许作为前缀或后缀。
    Include("~/Scripts/Common/*og.*") 无效的异常模式。只允许一个通配符。
    Include("~/Scripts/Common/T*") ToggleDiv.js,ToggleImg.js
    Include("~/Scripts/Common/*") 无效的异常模式。纯通配符段无效。
    IncludeDirectory("~/Scripts/Common","T*") ToggleDiv.js,ToggleImg.js
    IncludeDirectory("~/Scripts/Common","T*",true) ToggleDiv.js,ToggleImg.js,ToggleLinks.js

      一般当下面几种原因时显示的将每个文件添加到一个包中,要优先选用通配符加载文件:

    • 通过通配符增加脚本默认是按字母顺序加载它们的,这通常不是你想要的。CSS和JavaScript文件通常需要以特定的顺序(而非字母顺序)添加。你可以通过添加定制的IBundleOrderer实现来减少这种风险,但是现实的添加每个文件更不容易出错。例如,你可能会在将来添加一个新资源到一个文件夹中,这可能需要你修改IBundleOrderer的实现。
    • 特定视图的文件添加到目录时,使用通配符加载可能会被包含在所有引用该包的视图中。如果视图特定的脚本被添加到一个包中,在那些引用该包的视图中你可能会得到一个JavaScript错误。
    • 导入其他文件的CSS会导致导入的文件加载两次。例如,下面的代码创建了一个包,其中大多数JQuery UI主题的CSS文件加载了两次。
    bundles.Add(new StyleBundle("~/jQueryUI/themes/baseAll")
        .IncludeDirectory("~/Content/themes/base", "*.css"));

      通配符选择器“*.css”会选择文件夹中的每个CSS文件,包括Content hemesasejquery.ui.all.css文件。而jquery.ui.all.css文件已经导入了其他CSS文件。

      包缓存(Bundle Caching)

      当包(bundle)被创建时Bundles设置了HTTP过期头(HTTP Expires Header)为一年。如果你导航到一个先前查看的页面,Fiddler显示IE没有对bundle进行条件请求,也就是说,IE对包(bundle)没有HTTP GET请求,也没有HTTP 304响应来自服务器。你可以使用F5键(结果每个包(bundle)都有一个HTTP 304响应)来强制IE对每个bundle做出一个有条件的请求。你可以用F5来强制完全刷新(结果每个包(bundle)都有一个HTTP 200响应)。

      下面的图片显示了Fiddler响应窗格的缓存(Caching)选项卡:

      该请求:

    http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81

    是对AllMyScripts包的请求并包含了一个查询字符串对,v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81。查询字符串v具有一个值令牌,它是用于缓存的唯一标识。只要包(bundle)不改变,ASP.NET 应用程序将会用这个令牌(v)去请求AllMyScripts包。如果该包(bundle)里的任何文件发生改变,ASP.NET 优化(optimization)框架将会生成一个新的令牌,确保浏览器对该包的请求得到最新的包。

      如果你运行IE9 F12开发者工具然后导航到一个先前加载的页面,IE错误的显示对每个包的条件GET请求,并服务器返回HTTP 304。你可以阅读为什么IE9在确定是否有条件请求时存在问题的博客使用CDNs和过期时间来改善Web站点的性能Using CDNs and Expires to Improve Web Site Performance)。

      LESS,CoffeeScript,SCSS,Sass 打包(LESS,CoffeeScript,SCSS,Sass Bundling)

      打包和压缩框架提供了一种机制来处理诸如SCSSSassLESS  或 CoffeeScript 等中间语言,并应用转换,例如缩小到结果包(resulting bundle)。例如,添加 .less 文件到你的MVC 4项目中:

      1,创建一个你的LESS内容文件夹。下面的例子用的是ContentMyLess文件夹。

      2,添加 .less  NuGet包dotless到你的项目。

      3,添加一个类来实现IBundleTransform接口。为变换(transform).less,添加下面的代码到你的项目中。

    using System.Web.Optimization;
    
    public class LessTransform : IBundleTransform
    {
        public void Process(BundleContext context, BundleResponse response)
        {
            response.Content = dotless.Core.Less.Parse(response.Content);
            response.ContentType = "text/css";
        }
    }

      4,使用LessTransformCssMinify转换创建一个LESS文件包。在App_StartBundleConfig.cs文件的RegisterBundles方法中添加下面的代码。

    var lessBundle = new Bundle("~/My/Less").IncludeDirectory("~/My", "*.less");
    lessBundle.Transforms.Add(new LessTransform());
    lessBundle.Transforms.Add(new CssMinify());
    bundles.Add(lessBundle);

      5,在需要引用LESS包的视图中添加以下代码。

    @Styles.Render("~/My/Less");

      包注意事项(Bundle Considerations)

      在创建包时遵循的一个好习惯是将“bundle”包含在bundle名称的前缀中。这将防止可能的路由冲突。

      当你在一个包中更新一个文件时,将为bundle查询字符串参数生成一个新的令牌,并在下一次客户端请求包的页面时下载完整的包。在传统的标记中,每个资源是被单独列出的,只有更改后的文件才会被下载。对经常变化的资源进行打包(bundling)可能不是好的选择。

      打包和压缩主要是改善第一个页面的请求加载时间。一旦请求了一个网页,浏览器就会缓存这些资源(JavaScript,CSS和图像),因此打包和压缩在请求相同页面或请求同一站点上的相同资源时不会提供任何性能的提升。如果在你的资源上你没有正确的设置过期头(expires header),并且你不使用打包和压缩,浏览器的新鲜感(freshness heuristic)将会在几天后标记资源过期,并且浏览器对每个资源需要验证请求。在这种情况下,打包和压缩在第一个页面请求之后提供了性能提升。欲知详情,请参考使用CDNs和过期时间来改善Web站点的性能

      通过使用CDN,可以缓解每个主机名对6个并发连接的浏览器限制。因为CDN和你的主机站点有不同的主机名,来自CDN的资源请求不会对你的托管环境的6个并发连接限制进行计数。CDN还可以提供常用的包缓存和边缘缓存优势。

      包(bundles)应该根据页面对它们的需要进行分割。例如,默认的ASP.NET MVC模板创建了与jQuery分离的jQuery验证包。因为创建的默认视图没有输入,也没有post的值,所以它们不包含验证包。

      System.Web.Optimization命名空间是在System.Web.Optimization.DLL程序集中实现的。它利用WebGrease库(WebGrease.dll)来实现压缩(minification)功能,而这反过来又使用了Antlr3.Runtime.dll

      其他资源(Additional Resources)

      参考文献

      原文:https://docs.microsoft.com/en-us/aspnet/mvc/overview/performance/bundling-and-minification

  • 相关阅读:
    jQuery Mobile方向感应事件
    Linq-多条件查询
    linux top命令详解
    在Python中调用C++,使用SWIG
    linux下core文件调试方法
    如何设置、查看以及调试core文件
    标准C++中的string类的用法总结(转)
    实用make最佳实践
    GDB多进程调试(转)
    GDB详解
  • 原文地址:https://www.cnblogs.com/shangec/p/8824203.html
Copyright © 2011-2022 走看看