在缩小期间排除调试 JavaScript 代码

Posted

技术标签:

【中文标题】在缩小期间排除调试 JavaScript 代码【英文标题】:Exclude debug JavaScript code during minification 【发布时间】:2011-02-25 10:40:04 【问题描述】:

我正在研究缩小 javascript 代码的不同方法,包括常规的 JSMin、Packer 和 YUI 解决方案。我对新的Google Closure Compiler 非常感兴趣,因为它看起来异常强大。

我注意到Dean Edwards packer 具有排除以三个分号开头的代码行的功能。这很方便排除调试代码。例如:

;;;     console.log("Starting process");

我正在花一些时间清理我的代码库,并想添加这样的提示以轻松排除调试代码。在为此做准备时,我想弄清楚这是否是最佳解决方案,或者是否还有其他技术。

因为我还没有选择如何缩小,所以我想以与我最终使用的任何缩小器兼容的方式清理代码。所以我的问题是:

    使用分号是一种标准技术,还是有其他方法可以做到?

    Packer 是唯一提供此功能的解决方案吗?

    其他解决方案是否也可以采用这种方式工作,或者他们有替代方法来实现这一点?

    我最终可能会开始使用 Closure Compiler。我现在应该做些什么来为它做准备吗?

【问题讨论】:

【参考方案1】:

这是闭包编译器的(最终)答案:

/** @const */
var LOG = false;
...
LOG && log('hello world !'); // compiler will remove this line
...

这甚至可以与SIMPLE_OPTIMIZATIONS 一起使用,并且不需要--define=

【讨论】:

这个答案应该在顶部。一个好处是,在高级模式下,log 函数将作为死代码被删除。 这听起来很有用。但是在在线closure-compiler页面和local上测试是没有效果的。 @hellectronic 实际上它确实有效,您只需将所有代码放在一个闭包中,否则编译器会认为它是一个全局变量,并且不会将其剥离。无论如何,这可能是最好的,这样您就不会全球化任何“使用严格”的语句 与使用传统的console.log() 不同,此方法的一大缺点是,当LOG=truelog() 的开发工具中报告的行号是定义此函数的位置而不是位置它被调用了。因此,所有消息都报告为在同一行上,而不是它们发生的位置。考虑到消息的行号通常是调试的关键,这是相当不理想的。 对于任何对如何使这项工作感到困惑的人,这里有一个简单的方法:1)在顶部,在关闭设置之后,在您的代码之前插入/** @const */ var LOG = false;。 2) 在您的代码中查找/更改console.logLOG&&console.log。编译器将删除所有 console.log 调用。但是,对我来说,这只适用于“ADVANCED_OPTIMIZATIONS”模式【参考方案2】:

这是我在 Closure Compiler 中使用的。首先,您需要像这样定义一个 DEBUG 变量:

/** @define boolean */
var DEBUG = true;

它使用JS注解进行闭包,您可以阅读in the documentation。

现在,只要您需要一些仅用于调试的代码,只需将其包装在 if 语句中,如下所示:

if (DEBUG) 
  console.log("Running in DEBUG mode");

编译发布代码时,添加以下编译命令:--define='DEBUG=false' -- 调试语句中的任何代码都将完全排除在编译文件之外。

【讨论】:

不要错过注释'/** @define boolean */',没有它就无法工作。【参考方案3】:

在这种情况下,一个好的解决方案可能是js-build-tools,它支持“条件编译”。

总之你可以使用cmets比如

// #ifdef debug
var trace = debug.getTracer("easyXDM.Rpc");
trace("constructor");
// #endif

您在其中定义了诸如debug 之类的编译指示。

然后在构建它时(它有一个蚂蚁任务)

//this file will not have the debug code
<preprocess infile="work/easyXDM.combined.js" outfile="work/easyXDM.js"/>
//this file will        
<preprocess infile="work/easyXDM.combined.js" outfile="work/easyXDM.debug.js" defines="debug"/>

【讨论】:

【参考方案4】:

将逻辑添加到代码中您要登录到控制台的每个位置都会使调试和维护变得更加困难。

如果您已经准备为您的生产代码添加构建步骤,您始终可以在顶部添加另一个文件,将您的 console 方法转换为 noop 的方法。

类似:

console.log = console.debug = console.info = function();

理想情况下,您只需删除任何 console 方法,但如果您仍然保留它们但不使用它们,这可能是最容易使用的方法。

【讨论】:

【参考方案5】:

如果您在高级模式下使用闭包编译器,您可以执行以下操作:

if (DEBUG) console.log = function() 

然后编译器将删除所有 console.log 调用。当然你需要在命令行中--define变量DEBUG

不过,这仅适用于高级模式。如果您使用的是简单模式,则需要对源文件运行预处理器。

为什么不考虑 Dojo Toolkit?它具有内置的基于注释的编译指示,可以根据构建包含/排除代码部分。另外,它与高级模式下的闭包编译器兼容(见下面的链接)!

http://dojo-toolkit.33424.n3.nabble.com/file/n2636749/Using_the_Dojo_Toolkit_with_the_Closure_Compiler.pdf?by-user=t

【讨论】:

【参考方案6】:

尽管这是一个老问题。我今天偶然发现了同样的问题,发现使用CompilerOptions可以实现。

我关注了this thread。

在将代码发送到客户端之前,我们在服务器上运行 Java 编译器。这在简单模式下对我们有用。

private String compressWithClosureCompiler(final String code) 
    final Compiler compiler = new Compiler();
    final CompilerOptions options = new CompilerOptions();
    Logger.getLogger("com.google.javascript.jscomp").setLevel(Level.OFF);
    if (compressRemovesLogging) 
        options.stripNamePrefixes = ImmutableSet.of("logger");
        options.stripNameSuffixes = ImmutableSet.of("debug", "dev", "info", "error",
                "warn", "startClock", "stopClock", "dir");
    
    CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);

    final JSSourceFile extern = JSSourceFile.fromCode("externs.js", "");
    final JSSourceFile input = JSSourceFile.fromCode("input.js", code);
    compiler.compile(extern, input, options);
    return compiler.toSource();

它将删除对 logger.debug、logger.dev...etc.etc 的所有调用

【讨论】:

【参考方案7】:

如果您使用的是UglifyJS2,则可以使用 drop_console 参数来删除 console.* 函数。

【讨论】:

【参考方案8】:

我在我的 React 应用程序中使用它:

if (process.env.REACT_APP_STAGE === 'PROD')
  console.log = function no_console() ;

换句话说,console.log 不会在 prod 环境中返回任何内容。

【讨论】:

【参考方案9】:

我和@marcel-korpel 在一起。不完美但有效。在缩小之前替换调试指令。正则表达式在很多地方都有效。注意未封闭的线。

/console\.[^;]*/gm

适用于:

;;;     console.log("Starting process");
console.log("Starting process");
console.dir("Starting process");;;;;
console.log("Starting "+(1+2)+" processes"); iamok('good');
console.log('Message ' +
    'with new line'
);
console.group("a");
console.groupEnd();
swtich(input)
    case 1 : alert('ok'); break;
    default: console.warn("Fatal error"); break;

不工作:

console.log("instruction without semicolon")
console.log("semicolon in ; string");

【讨论】:

【参考方案10】:

到目前为止,我还没有研究过缩小,但这种行为可以使用简单的正则表达式来完成:

s/;;;.*//g

这会将一行中三个分号之后(包括)的所有内容替换为空,因此在缩小之前将其丢弃。您可以在运行缩小工具之前运行sed(或类似工具),如下所示:

sed 's/;;;.*//g' < infile.js > outfile.js

顺便说一句,如果您想知道打包版本还是缩小版本会“更好”,请阅读this comparison of JavaScript compression methods。

【讨论】:

【参考方案11】:

我用过以下自制的东西:

// Uncomment to enable debug messages
// var debug = true;

function ShowDebugMessage(message) 
    if (debug) 
        alert(message);
    

因此,当您声明变量 debug 并将其设置为 true - 所有 ShowDebugMessage() 调用也会调用 alert()。因此,只需在代码中使用它,就可以忘记 ifdef 或调试输出行的手动注释等原地条件。

【讨论】:

以上是关于在缩小期间排除调试 JavaScript 代码的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular/Javascript 中排除假期日期

Javascript 缩小缓存问题

JavaScript相关

如何在 ASP.NET MVC 4 Beta 中禁用 Javascript/CSS 缩小

在 Rails 生产中禁用资产缩小

在上传到远程网站期间缩小/调整视频大小