在压缩过程中排除调试JavaScript代码

47
我正在探索不同的方法来压缩我的JavaScript代码,包括常规的JSMin, Packer和YUI解决方案。我对新的Google Closure Compiler非常感兴趣,因为它看起来异常强大。
我注意到Dean Edwards packer具有一项功能,可以排除以三个分号开头的代码行。这很方便,可以排除调试代码。例如:
;;;     console.log("Starting process");

我正在清理我的代码库,想要添加像这样的提示以轻松排除调试代码。为此,我想弄清楚这是否是最佳解决方案,或者是否有其他技术可用。
因为我还没有选择如何缩小代码,所以我希望以与我最终选择的缩小器兼容的方式来清理代码。所以我的问题是:
1. 使用分号是一种标准技术吗?还有其他方法吗? 2. Packer是唯一提供此功能的解决方案吗? 3. 其他解决方案是否也可以适应这种方式工作,还是它们有实现此目的的替代方法? 4. 我可能会开始使用Closure Compiler。现在有什么我应该做的来为此做好准备吗?
12个回答

65

这是 Closure Compiler 的(终极)答案:

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

即使使用SIMPLE_OPTIMIZATIONS也可以正常工作,而且不需要--define=


3
这个答案应该置于顶部。在高级模式下的额外好处是log函数将被剥离作为死代码。 - Matthew
有人能更详细地解释一下这个答案吗?我对闭包编译器还很陌生,不太明白这段代码在做什么。 - tim peterson
嗨,@Kares,你是不是指的 console.log('...') 而不是 log('...')?使用 log() 会出错。另外,如果需要在多个文件中使用,如 @anisart 所说,它需要在闭包中运行,那么它该如何工作呢?您必须在每个页面上都放置您的代码吗? - tim peterson
3
与传统的console.log()不同,这种方法的一个重要缺点在于,当LOG=true时,开发工具中报告的log()行号是该函数定义的位置而不是调用位置。因此,所有消息都报告为在同一行上,而不是它们发生的位置。这很糟糕,因为消息的行号通常对于调试非常关键。 - tim peterson
2
对于任何对如何使其工作感到困惑的人,这里有一个简单的方法:1)在闭包设置之后,在您的代码之前放置 /** @const */ var LOG = false;。2)在您的代码中查找/更改 console.logLOG&&console.log。所有的 console.log 调用将被编译器移除。但是,对我来说,这仅适用于 "ADVANCED_OPTIMIZATIONS" 模式。 - user56reinstatemonica8
显示剩余4条评论

25

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

Here's what I use with Closure Compiler. First, you need to define a DEBUG variable like this:

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

它使用了JS的闭包注释,您可以在文档中阅读相关内容。

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

if (DEBUG) {
  console.log("Running in DEBUG mode");
}
当您为发布编译代码时,请在编译命令中添加以下内容:--define='DEBUG=false',任何位于调试语句内的代码都将完全排除在编译文件之外。

1
不要忘记注释 '/** @define {boolean} */',没有它将无法工作。 - Palani

6
在这种情况下,一个好的解决方案可能是js-build-tools,它支持“条件编译”。
简而言之,您可以使用注释,例如:
// #ifdef debug
var trace = debug.getTracer("easyXDM.Rpc");
trace("constructor");
// #endif

你需要定义一个类似于debug的编译指示符。

接着,在构建过程中(它有一个ant任务),

//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"/>

6
在你的代码中每个记录到控制台的地方添加逻辑将使调试和维护变得更加困难。
如果您已经为生产代码添加了构建步骤,那么您可以在顶部再添加另一个文件,将您的console方法转换为noop。
类似于:
console.log = console.debug = console.info = function(){};

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

2
尽管这是一个老问题,但今天我偶然遇到了同样的问题,并发现可以使用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等的调用...等等。

2
如果您在高级模式下使用 Closure 编译器,您可以进行如下操作:
if (DEBUG) console.log = function() {}

然后编译器将删除所有的console.log调用。当然,你需要在命令行中--define变量DEBUG
然而,这只适用于高级模式。如果你使用简单模式,则需要在源文件上运行预处理器。
为什么不考虑使用Dojo Toolkit呢?它内置了基于注释的pragma,可以根据构建包含/排除代码部分。此外,它与Closure Compiler在高级模式下兼容(见下面的链接!)。 http://dojo-toolkit.33424.n3.nabble.com/file/n2636749/Using_the_Dojo_Toolkit_with_the_Closure_Compiler.pdf?by-user=t

2

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


2

我在我的React应用程序中使用这个:

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

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

1

我和@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");

0
我使用了以下自制的东西:
// Uncomment to enable debug messages
// var debug = true;

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

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


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