【问题标题】:How can I specify an explicit ScriptBundle include order?如何指定明确的 ScriptBundle 包含顺序?
【发布时间】:2012-08-12 08:42:50
【问题描述】:

我正在试用MVC4 System.Web.Optimization 1.0 ScriptBundle feature

我有以下配置:

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        // shared scripts
        Bundle canvasScripts =
            new ScriptBundle(BundlePaths.CanvasScripts)
                .Include("~/Scripts/modernizr-*")
                .Include("~/Scripts/json2.js")
                .Include("~/Scripts/columnizer.js")
                .Include("~/Scripts/jquery.ui.message.min.js")
                .Include("~/Scripts/Shared/achievements.js")
                .Include("~/Scripts/Shared/canvas.js");
        bundles.Add(canvasScripts);
    }
}

还有以下观点:

<script type="text/javascript" src="@Scripts.Url(BundlePaths.CanvasScripts)"></script>

其中BundlePaths.CanvasScripts"~/bundles/scripts/canvas"。它呈现这个:

<script type="text/javascript" src="/bundles/scripts/canvas?v=UTH3XqH0UXWjJzi-gtX03eU183BJNpFNg8anioG14_41"></script>

到目前为止一切顺利,除了 ~/Scripts/Shared/achievements.js 是捆绑源代码中的第一个脚本。这取决于ScriptBundle 中包含的每个脚本。 如何确保它遵守我将包含语句添加到包中的顺序?

更新

这是一个相对较新的 ASP.NET MVC 4 应用程序,但它引用了优化框架预发布包。我删除了它并从http://nuget.org/packages/Microsoft.AspNet.Web.Optimization 添加了RTM 包。对于 web.config 中带有 debug=true 的 RTM 版本,@Scripts.Render("~/bundles/scripts/canvas") 以正确的顺序呈现各个脚本标签。

在 web.config 中使用 debug=false 时,组合脚本首先具有成就.js 脚本,但由于它是稍后调用的函数定义(对象构造函数),因此它运行时不会出错。也许 minifier 足够聪明,可以找出依赖关系?

我还尝试了 Darin Dimitrov 建议的 IBundleOrderer 实现,带有两个调试选项的 RTM,它的行为相同。

所以缩小版不是我期望的顺序,但它可以工作。

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-4 bundle asp.net-optimization


    【解决方案1】:

    您可以编写一个自定义捆绑订购程序 (IBundleOrderer),以确保捆绑包含在您注册它们的顺序中:

    public class AsIsBundleOrderer : IBundleOrderer
    {
        public virtual IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
        {
            return files;
        }
    }
    

    然后:

    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            var bundle = new Bundle("~/bundles/scripts/canvas");
            bundle.Orderer = new AsIsBundleOrderer();
            bundle
                .Include("~/Scripts/modernizr-*")
                .Include("~/Scripts/json2.js")
                .Include("~/Scripts/columnizer.js")
                .Include("~/Scripts/jquery.ui.message.min.js")
                .Include("~/Scripts/Shared/achievements.js")
                .Include("~/Scripts/Shared/canvas.js");
            bundles.Add(bundle);
        }
    }
    

    在你看来:

    @Scripts.Render("~/bundles/scripts/canvas")
    

    【讨论】:

    • 这应该可以,但也应该是不必要的,因为默认排序器通常尊重包含的顺序(它只会将一些特殊文件提升到顶部,即 jquery、reset.css/normalize .css 等)。它不应该将成就.js 提升到顶部。
    • @flipdoubt 在此处提交问题与 repro:aspnetoptimization.codeplex.com/workitem/list/advanced
    • 这很完美,谢谢!不过我同意,这应该没有必要。
    • 这对我有用。当根据stackoverflow.com/questions/17367736/… 我需要翻转它时,它正在通过引导程序推广 jqueryui。
    • @Hao Kung 可以订购include目录下的文件吗?
    【解决方案2】:

    我在 RTM 位上没有看到这种行为,您是否使用 Microsoft ASP.NET Web 优化框架 1.0.0 位:http://nuget.org/packages/Microsoft.AspNet.Web.Optimization

    我使用了与您的示例类似的复制品,基于一个新的 MVC4 Internet 应用程序网站。

    我添加到 BundleConfig.RegisterBundles:

            Bundle canvasScripts =
                new ScriptBundle("~/bundles/scripts/canvas")
                    .Include("~/Scripts/modernizr-*")
                    .Include("~/Scripts/Shared/achievements.js")
                    .Include("~/Scripts/Shared/canvas.js");
            bundles.Add(canvasScripts); 
    

    然后在默认的索引页面中,我添加了:

    <script src="@Scripts.Url("~/bundles/scripts/canvas")"></script>
    

    我验证了在压缩包的 javascript 中,results.js 的内容是在modernizr 之后...

    【讨论】:

    • 我升级到了1.0版本。请参阅我更新的问题。 results.js 的内容首先包含在缩小版本中,但它可以工作,因为它是一个稍后调用的函数。
    【解决方案3】:

    谢谢达林。我添加了一个扩展方法。

    internal class AsIsBundleOrderer : IBundleOrderer
    {
        public virtual IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
        {
            return files;
        }
    }
    
    internal static class BundleExtensions
    {
        public static Bundle ForceOrdered(this Bundle sb)
        {
            sb.Orderer = new AsIsBundleOrderer();
            return sb;
        }
    }
    

    用法

    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js",
                        "~/Scripts/jquery-migrate-{version}.js",
    
                        "~/Scripts/jquery.validate.js",
                        "~/Scripts/jquery.validate.messages_fr.js",
                        "~/Scripts/moon.jquery.validation-{version}.js",
    
                        "~/Scripts/jquery-ui-{version}.js"
                        ).ForceOrdered());
    

    【讨论】:

    • 相当漂亮! :D 现在我必须在接口方法签名中将FileInfo 更改为BundleFile。他们改变了它。
    • 是的,他们在使用 Owin 框架的预发布版本中对其进行了更改
    • 小题外话:有人知道这个BundleFile 课程的更多信息吗?为了捆绑嵌入式资源,我正在尝试对其进行实例化,它需要在其构造函数中使用 VirtualFile 的(具体子代)参数。
    • 我也有同样的问题,我需要实例化BundleFile,但不知道怎么做
    • @superjos 我知道这已经很老了,但我会回答以防其他人有问题。它是 System.Web.Optimization 的一部分。
    【解决方案4】:

    您应该能够在 BundleCollection.FileSetOrderList 的帮助下设置顺序。看看这篇博文:http://weblogs.asp.net/imranbaloch/archive/2012/09/30/hidden-options-of-asp-net-bundling-and-minification.aspx。您实例中的代码类似于:

    BundleFileSetOrdering bundleFileSetOrdering = new BundleFileSetOrdering("js");
    bundleFileSetOrdering.Files.Add("~/Scripts/modernizr-*");
    bundleFileSetOrdering.Files.Add("~/Scripts/json2.js");
    bundleFileSetOrdering.Files.Add("~/Scripts/columnizer.js");
    bundleFileSetOrdering.Files.Add("~/Scripts/jquery.ui.message.min.js");
    bundleFileSetOrdering.Files.Add("~/Scripts/Shared/achievements.js");
    bundleFileSetOrdering.Files.Add("~/Scripts/Shared/canvas.js");
    bundles.FileSetOrderList.Add(bundleFileSetOrdering);
    

    【讨论】:

      【解决方案5】:

      @Darin Dimitrov 的答案非常适合我,但我的项目是用 VB 编写的,所以这是他的答案转换为 VB

      Public Class AsIsBundleOrderer
          Implements IBundleOrderer
      
          Public Function IBundleOrderer_OrderFiles(ByVal context As BundleContext, ByVal files As IEnumerable(Of FileInfo)) As IEnumerable(Of FileInfo) Implements IBundleOrderer.OrderFiles
              Return files
          End Function
      End Class
      

      使用它:

      Dim scriptBundleMain = New ScriptBundle("~/Scripts/myBundle")
      scriptBundleMain.Orderer = New AsIsBundleOrderer()
      scriptBundleMain.Include(
          "~/Scripts/file1.js",
          "~/Scripts/file2.js"
      )
      bundles.Add(scriptBundleMain)
      

      【讨论】:

      • 我不断收到此错误:Class 'AsIsBundleOrderer' must implement 'Function OrderFiles(context as ....)' for interface 'System.Web.Optimization.IBundleOrderer' 有什么想法吗?
      【解决方案6】:

      您应该考虑使用 Cassette http://getcassette.net/ 它支持根据在每个文件中找到的脚本引用自动检测脚本依赖关系。

      如果您更喜欢坚持 MS Web 优化解决方案,但喜欢根据脚本参考安排脚本的想法,您应该阅读我的博客文章http://blogs.microsoft.co.il/oric/2013/12/27/building-single-page-application-bundle-orderer/

      【讨论】:

        【解决方案7】:

        更新了 SoftLion 提供的答案以处理 MVC 5 中的更改(BundleFile 与 FileInfo)。

        internal class AsIsBundleOrderer : IBundleOrderer
        {
            public virtual IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
            {
                return files;
            }
        }
        
        internal static class BundleExtensions
        {
            public static Bundle ForceOrdered(this Bundle sb)
            {
                sb.Orderer = new AsIsBundleOrderer();
                return sb;
            }
        }
        

        用法:

            bundles.Add(new ScriptBundle("~/content/js/site")
                .Include("~/content/scripts/jquery-{version}.js")
                .Include("~/content/scripts/bootstrap-{version}.js")
                .Include("~/content/scripts/jquery.validate-{version}")
                .ForceOrdered());
        

        我喜欢使用流畅的语法,但它也适用于单个方法调用和所有作为参数传递的脚本。

        【讨论】:

        • 在 MVC 5 中工作。谢谢
        • 优秀。这应该包含在 MS 恕我直言
        • 在 Web 表单中也像魅力一样工作......基本上它应该适用于所有最新版本!
        • 非常干净和简单的解决方案。非常感谢。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-04
        • 2021-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多