Use Google closure compiler to compress code

Source: Internet
Author: User

The importance of JavaScript code compression is self-evident. There are also many compression tools today, such as Yui compressor, Google closure compiler, and uglifyjs, which is currently booming. Uglifyjs is famous for its replacement of closure compiler as a compression tool for jquery projects. According to my tests, the jquery core code is actually smaller (62.5% saved) after uglifyjs compression than closure compiler compression (57.53% saved. Obviously, this is because uglifyjs's compression policy is more "smarter" than closure compiler. I use "smart" instead of "radical" here, because "radical" carries a negative sign-like closure Compiler's "advanced" optimization method. Compared with uglifyjs, closure Compiler's "simple" optimization methods are "secure", while closure Compiler's "advanced" optimization will destroy your code by almost 100%, therefore, it proposes various "radical" means to "destroy" your code to achieve the goal of compression. This method is a double-edged sword. If you can control its compression rules, the code can be compressed to a minimum.

Let's take a look at the power of closure compiler. For example, I have the following code:

var Jscex = (function () {    /**    * @constructor    */    var CodeGenerator = function () {        this.normalMode = false;    }    CodeGenerator.prototype.generate = function () {        alert("Hello World");    }    function compile() {        return new CodeGenerator();    };    return { compile: compile };})();

I guess what will happen if I use closure Compiler's advanced optimization method to compress code? The result is as follows:

(function(){function a(){this.a=!1}return{compile:function(){return new a}}})();

The target code is very short. It's okay to stick your head to it. First, the jscex object disappears because closure compiler thinks this object is not used elsewhere. Second, the normalmode field of codegenerator is also renamed as a because the name saves space. Finally, the generate method is missing for the same reason as the first one. Are you sure you want to execute this code? This is where closure compiler is radical. It regards the input file as a complete unit and does not consider whether the external "interface" will change. I read the closure compiler document and found that it supports marking the source code. However, after experiment, these labels do not seem to affect the compiled results, but they only allow the compiler to perform some "static checks" during work ".

Of course, in theory, closure compiler provides a mechanism to keep member names, such as exports and extern. If I want to keep the previous jscex object, I must do this:

window["Jscex"] = (function () { ... })();

In this way, the code generated by closure compiler becomes:

window.Jscex=(function(){ ... })();

To "save" space, it is so painful to switch the "Index" access method back to the "field" access method! In addition, I thought closure compiler forced me to rely on the browser environment. Later I found that Windows can also be replaced with other names, for example:

my_root["Jscex"] = (function () {    /**    * @constructor    */    var CodeGenerator = function () {        this["normalMode"] = false;    }    CodeGenerator.prototype["generate"] = function () {        alert("Hello World");    }    function compile() {        return new CodeGenerator();    };    return { compile: compile };})();

Although theoretically speaking, using this method can tell closure Compiler which member names can be compressed and which ones cannot be compressed, it is hard for me to accept this method of "indexing. However, in fact, this has little impact on me, because I seldom use the "Object-Oriented" method to open interfaces to the outside world, in general, I use the form of "object" and "method", such as jscex above. for the compile method, as for the internal type, such as codegenerator, it will be compressed with closure compiler.

Then again, if you write JavaScript code from the beginning and follow certain rules, closure compiler can indeed compress your code very small. You can even write a little more debugging code, but remove them from the compressed code. The most basic principle can be summarized as follows: extract the code that is not needed after compression into an independent method, and then remove the call code of these methods in the preprocessing phase, therefore, closure compiler deletes the definitions of these methods, saving a lot of space.

Take the jscex project as an example: one of the core of jscex is to generate JavaScript code based on AST. In the implementation of the "debug" version, I hope that the generated code will be beautiful and easy to read; in the "release" version, I hope that the smaller the size of the Code, the better. Therefore, the scenario where an expression "whether to add parentheses" needs to be considered in detail. My policy is to place the logic that determines whether to add parentheses in the needbracket method in the "debug" code, and then write the code like this:

"dot": function (ast) {    function needBracket() { /* ... */ }    var nb = needBracket();    if (nb) {        this._write("(")            ._visit(ast[1])            ._write(").")            ._write(ast[2]);    } else {        this._visit(ast[1])            ._write(".")            ._write(ast[2]);    }},

The above method is used to generate code for a dot expression, which defines the needbracket method. We can place complicated and inefficient logic in it, used to determine whether parentheses need to be added to the left expression of dot. If needbracket returns true, parentheses are generated, for example ("ABC" + "def "). otherwise, code that is more concise and easy to read will be generated, such as jscex. async. instead of (jscex ). async ). start. However, in the Code of the final "release" version, the Nb variable is directly set to true, so closure compiler will find that a branch of if will never be executed, then it will be completely removed. In the compressed code, the above method will only be like this:

dot:function(a){this.a("(").b(a[1]).a(").").a(a[2])},

It can be seen that this implementation will generate JavaScript code with parentheses anyway. ugly, but there is no difference in the JavaScript engine. The current jscex. js compression script is actually like this:

# pre-processing for Closure Compilersed     -e ‘s/var Jscex =/my_temp_root["Jscex"] =/‘     -e ‘s/\._writeLine(/._write(/g‘     -e ‘s/this\._write();//g‘     -e ‘s/\._write()//g‘     -e ‘s/this\._writeIndents();//g‘     -e ‘s/\._writeIndents()//g‘     -e ‘s/this\._indentLevel = 0;//g‘     -e ‘s/this\._indentLevel++;//g‘     -e ‘s/this\._indentLevel--;//g‘     -e ‘s/checkBindArgs([^;]*;//g‘     -e ‘s/needBracket([^;]*;/true;/g‘     -e ‘s/throwUnsupportedError();//g‘     -e ‘s/_log([^;]*;//g‘     ../src/jscex.js > ../bin/jscex.tmp.js# use Closure Compiler to compressjava     -jar ../tools/compiler.jar     --js ../bin/jscex.tmp.js     --js_output_file ../bin/jscex.tmp.min.js     --compilation_level ADVANCED_OPTIMIZATIONS# post-processingsed ‘s/my_temp_root\.Jscex=/var Jscex=/‘ ../bin/jscex.tmp.min.js > ../bin/jscex.min.js# remove temp filesrm ../bin/jscex.tmp*.js

Before using closure compiler to compress the code, I perform "preprocessing" on the script. The following items are provided:

  • To avoid the loss of the jscex object, replace var jscex with my_temp_root ["jscex"], compress it, and replace it.
  • Replace all writeline method calls with write, so that the writeline method is not used in the code, and closure compiler removes the definition of this method.
  • Removes the empty write method call, which is generally caused by the replacement of writeline with write.
  • Remove all attributes and methods related to "indent", so that the related definitions will disappear after compression.
  • Remove various error checks, such as calling the checkbindargs and throwunsupportederror methods.
  • Remove the log output, that is, the _ LOG method is called, and the _ LOG method itself disappears.
  • Replace the needbracket method call with true to force the Code with parentheses to be output.

Using this method, we can take full advantage of closure Compiler's radical compression at the "advanced" optimization level, and get the correct, efficient, and small size code (Supplement: later, we found that in some cases, we can use the method of defining constants to simplify preprocessing ). Compare jscex. js with jquery core (note and blank characters are removed in advance ):

  • Simple compressed jquery core (secure compression): reduces the size by 30.83% (120.18kb => 83.13kb ).
  • "Advanced" compression of jquery core (unsecure compression, unavailable): reduces the size by 37.91% (120.18kb => 74.62kb ).
  • "Advanced" compression of jscex. js (unsecure compression, available): reduces the size by 55.02% (12.14kb => 5.46kb ).

The above data is obtained from the online closure compiler, and I don't know why the effect is not as good as that on the local device. From the perspective of local compression, jscex. JS is 25812 bytes, while jscex. Min. JS is only 5585 bytes, which is nearly five times different.

Unfortunately, if we do not consider closure Compiler's many behaviors from the beginning of code writing and compression, we can only use a "simple" compression method to ensure the correctness of the Code. It is almost impossible to make a large piece of code (such as jquery) Pass the "advanced" Test of closure compiler.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.