在使用微软 Web 优化框架时,不要使某些文件变丑。

14
我试图使用 Microsoft Web Optimization framework 将许多 .js 文件合并成一个文件。一切正常,但在这些文件中,我有几个已经被压缩和混淆的文件,没有必要再次处理它们。
例如,我有 recaptcha_ajax.js 文件,在将其附加时会导致以下错误:
/* Minification failed. Returning unminified contents.
(715,29-36): run-time error JS1019: Can't have 'break' outside of loop: break t
(714,293-300): run-time error JS1019: Can't have 'break' outside of loop: break t
(678,210-217): run-time error JS1019: Can't have 'break' outside of loop: break t
(671,1367-1374): run-time error JS1019: Can't have 'break' outside of loop: break t
(665,280-287): run-time error JS1019: Can't have 'break' outside of loop: break t
 */

我尝试将recaptcha_ajax.js从bundle中取出并直接引用,但是会出现其他错误 - 因此,我需要在bundle中的特定位置放置该文件。
我只需要能够说 - 不要压缩和混淆recaptcha_ajax.js - 只需将其添加到bundle中。
有没有办法做到这一点?这是我的想法:
var b = new ScriptBundle("~/bundles/myjsbundle");

b.IncludeDirectory("~/ScriptsMine/", "*.js", true);

// some command like:
// b.DoNotMinifyOrUglify("~/ScriptsMine/recaptcha_ajax.js");

bundles.Add(b);

你在脚本管理器中打包吗? - fnostro
3个回答

27

通过使用一系列的IItemTransform进行文件转换,并将结果进行串联,包将其转换为IBundleTransform集合来进一步转换该结果。

默认的脚本包使用JsMinify(实现了IBundleTransform)对整个包内容进行缩小。

因此,如果要防止某些文件被缩小,则需要创建自己的IBundleBuilder,通过使用IItemTransform逐个文件地对包进行缩小。

public class CustomScriptBundle : Bundle
{
    public CustomScriptBundle(string virtualPath)
        : this(virtualPath, null)
    {
    }

    public CustomScriptBundle(string virtualPath, string cdnPath)
        : base(virtualPath, cdnPath, null)
    {
        this.ConcatenationToken = ";" + Environment.NewLine;
        this.Builder = new CustomBundleBuilder();
    }
}


public class CustomBundleBuilder : IBundleBuilder
{
    internal static string ConvertToAppRelativePath(string appPath, string fullName)
    {
        return (string.IsNullOrEmpty(appPath) || !fullName.StartsWith(appPath, StringComparison.OrdinalIgnoreCase) ? fullName : fullName.Replace(appPath, "~/")).Replace('\\', '/');
    }

    public string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<BundleFile> files)
    {
        if (files == null)
            return string.Empty;
        if (context == null)
            throw new ArgumentNullException("context");
        if (bundle == null)
            throw new ArgumentNullException("bundle");

        StringBuilder stringBuilder = new StringBuilder();
        foreach (BundleFile bundleFile in files)
        {
            bundleFile.Transforms.Add(new CustomJsMinify());
            stringBuilder.Append(bundleFile.ApplyTransforms());
            stringBuilder.Append(bundle.ConcatenationToken);
        }

        return stringBuilder.ToString();
    }
}

public class CustomJsMinify : IItemTransform
{
    public string Process(string includedVirtualPath, string input)
    {
        if (includedVirtualPath.EndsWith("min.js", StringComparison.OrdinalIgnoreCase))
        {
            return input;
        }

        Minifier minifier = new Minifier();
        var codeSettings = new CodeSettings();
        codeSettings.EvalTreatment = EvalTreatment.MakeImmediateSafe;
        codeSettings.PreserveImportantComments = false;

        string str = minifier.MinifyJavaScript(input, codeSettings);

        if (minifier.ErrorList.Count > 0)
            return "/* " + string.Concat(minifier.Errors) + " */";

        return str;
    }
}

然后使用CustomScriptBundle而不是ScriptBundle

public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add(new CustomScriptBundle("~/bundles/Sample").Include(
                "~/Scripts/a.js",
                "~/Scripts/b.js",
                "~/Scripts/c.js"));
}

如果您提供了一个min.js文件,它将被用来代替对其进行缩小处理。


这段代码没有处理包含库的min和非min版本的情况,对吗?应该使用配置文件中的<compilation debug>来决定使用哪个版本。可以在BuildBundleContent方法的foreach之前添加此处理过程。 - Francesc Castells
这段代码没有完全测试,因此您可能需要进行一些更改才能使其按照您的要求工作。 - meziantou

3
优化框架会考虑文件名。
尝试将您的*.js文件重命名为recaptcha_ajax.min.js。如果我是正确的,那么它应该跳过uglify/minify过程。
参考数据:http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification(向下滚动一点,找到这个引用)
前面的代码创建了一个名为~/bundles/jquery的新JavaScript包,其中包括匹配通配符字符串“~/Scripts/jquery-{version}.js”的Scripts文件夹中的所有适当的文件(即调试或压缩但不是.vsdoc文件)。对于ASP.NET MVC 4,这意味着在调试配置中,将添加jquery-1.7.1.js文件到包中。在发布配置中,将添加jquery-1.7.1.min.js文件。捆绑框架遵循几个常见的约定,例如: - 当存在“FileX.min.js”和“FileX.js”时,在发布时选择“.min”文件。 - 在调试时选择非“.min”版本。 - 忽略“-vsdoc”文件(如jquery-1.7.1-vsdoc.js),这些文件仅由IntelliSense使用。

如果它能像这样工作就太棒了。但是,似乎它并没有 - 我已经创建了单独的hello.js和hello.min.js来进行测试,但无论名称中是否有“.min”,它都会被压缩。也许我错过了某些设置? - nikib3ro
你尝试在发布模式下运行它了吗?这样应该可以正常工作 :s - Yoeri
4
我认为这只是糟糕的工程设计 - 网站优化框架有太多开关,看起来直接更改捆绑构建过程是唯一控制它的方法。例如:无论是调试模式还是发布模式,BundleTable.EnableOptimizations都具有优先权(这是件好事)。如果BundleTable.EnableOptimizations = false,则会选择script.js文件。对于true,则选择script.min.js。但是 - 两个文件都将被压缩,而不管文件名如何。我希望他们能开源代码,这样我们就可以帮助他们做出一些设计决策(比如不要为文件名中包含.min.js的文件进行压缩)。 - nikib3ro

1
如果您在ScriptManager中捆绑.js文件,您会注意到脚本是在<form>中加载的,而不是在<head>中加载的,因此将其从捆绑包中移出会在其他文件之前加载该文件,如果它依赖于捆绑包中的其他内容,则不好。这是一个需要按特定顺序添加到捆绑包中的库的示例。
这将添加到App_Start/BundleConfig.vb中。
'############################################################
' This is a ScriptManager Resource Definition 
' for use in a ScriptManager mapping
'############################################################
    Dim ResourceDef as ScriptResourceDefinition = New ScriptResourceDefinition()
    Dim ResourceName as  String = "ColorBox"

    'add the Resource Definition details
    ResourceDef.Path = "~/Scripts/colorbox/jquery.colorbox-min.js"
    ResourceDef.DebugPath = "~/Scripts/colorbox/jquery.colorbox.js"

    ScriptManager.ScriptResourceMapping.AddDefinition(ResourceName, ResourceDef)
'############################################################

请注意使用 ResourceDef.PathResourceDef.DebugPath。包含哪个文件取决于您是进行 Debug 还是 Release 发布。
  • Debug: 不进行“丑化”
  • Release: 完全“丑化”

这是我的 ScriptManager 组合,注意 ColorBox 的位置,位置很重要:

<asp:ScriptManager runat="server" >
    <Scripts>
        <asp:ScriptReference Name="jquery" />
        <asp:ScriptReference Name="jquery.ui.combined" />
        <asp:ScriptReference Name="ColorBox" />          
        <asp:ScriptReference Name="Infragistics" /> 
        <asp:ScriptReference Name="MsAjaxBundle" />
        <asp:ScriptReference Name="WebForms.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebForms.js" />
        <asp:ScriptReference Name="WebUIValidation.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebUIValidation.js" />
        <asp:ScriptReference Name="MenuStandards.js" Assembly="System.Web" Path="~/Scripts/WebForms/MenuStandards.js" />
        <asp:ScriptReference Name="GridView.js" Assembly="System.Web" Path="~/Scripts/WebForms/GridView.js" />
        <asp:ScriptReference Name="DetailsView.js" Assembly="System.Web" Path="~/Scripts/WebForms/DetailsView.js" />
        <asp:ScriptReference Name="TreeView.js" Assembly="System.Web" Path="~/Scripts/WebForms/TreeView.js" />
        <asp:ScriptReference Name="WebParts.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebParts.js" />
        <asp:ScriptReference Name="Focus.js" Assembly="System.Web" Path="~/Scripts/WebForms/Focus.js" />
        <asp:ScriptReference Name="WebFormsBundle" />
    </Scripts>
</asp:ScriptManager>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接