使用 Closure Compiler Service API 压缩文件

Closure 编译器服务已弃用,并将被移除。请考虑改为在本地运行编译器。

概览

与 API 通信介绍了有关如何与 Closure Compiler 服务进行通信的基础知识,但仅阐述了如何使用该服务从单行 JavaScript 中移除注释。本教程介绍如何在更现实的开发场景中使用 Closure 编译器服务:处理整个 JavaScript 文件以显著减小大小。

本教程假定您对 JavaScript 和 HTTP 有基本的了解。虽然它使用 Python 脚本将 JavaScript 提交到 Closure Compiler 服务,但您无需了解 Python 即可遵循示例。

  1. 压缩文件
  2. 改善压缩效果
    1. 代码有多大?
    2. Closure 编译器服务如何使程序更小?
  3. 后续步骤

压缩文件

与 API 通信中的示例将一个 JavaScript 字符串作为命令行参数传递到我们的编译脚本。但是,这种方法不适用于实际大小的 JavaScript 程序,因为如果代码超过几行,JavaScript 字符串很快就会变得非常庞大。对于较大的程序,您可以使用 code_url 请求参数指定要处理的 JavaScript 文件的名称。除了 js_code 之外,您还可以使用 code_url,也可以将其用于 js_code

例如,请参考以下 JavaScript 程序:

/**
 * A simple script for adding a list of notes to a page. The list diplays
 * the text of each note under its title.
 */

/**
 * Creates the DOM structure for a note and adds it to the document.
 */
function makeNoteDom(noteTitle, noteContent, noteContainer) {
  // Create DOM structure to represent the note.
  var headerElement = document.createElement('div');
  var headerText = document.createTextNode(noteTitle);
  headerElement.appendChild(headerText);

  var contentElement = document.createElement('div');
  var contentText = document.createTextNode(noteContent);
  contentElement.appendChild(contentText);

  var newNote = document.createElement('div');
  newNote.appendChild(headerElement);
  newNote.appendChild(contentElement);

  // Add the note's DOM structure to the document.
  noteContainer.appendChild(newNote);
}

/**
 * Iterates over a list of note data objects and creates a DOM
 */
function makeNotes(data, noteContainer) {
  for (var i = 0; i < data.length; i++) {
    makeNoteDom(data[i].title, data[i].content, noteContainer);
  }
}

function main() {
  var noteData = [
      {title: 'Note 1', content: 'Content of Note 1'},
      {title: 'Note 2', content: 'Content of Note 2'}];
  var noteListElement = document.getElementById('notes');
  makeNotes(noteData, noteListElement);
}

main();

您可以将此程序作为文件更方便地作为 Closure 编译器服务传递给一个大字符串。请按照以下步骤使用该服务处理文件:

  1. 将 JavaScript 保存在文件中。
  2. 将文件设置为可通过网络访问(例如,将其上传到您的网络服务器)。
  3. 向 Closure Compiler 服务发出 POST 请求(如与 API 通信中所述),但对于 js_code 参数,请替换为 code_url 参数。值 code_url 必须是第 1 步中创建的 JavaScript 文件的网址。

例如,您可以在文件 tutorial2.js 中找到此示例的 JavaScript。如需使用 Closure Compiler Service API 处理此文件,请将 Python 程序从与 API 通信更改为使用 code_url,如下所示:

#!/usr/bin/python2.4

import httplib, urllib, sys

# Define the parameters for the POST request and encode them in
# a URL-safe format.

params = urllib.urlencode([
    ('code_url', sys.argv[1]), # <--- This parameter has a new name!
    ('compilation_level', 'WHITESPACE_ONLY'),
    ('output_format', 'text'),
    ('output_info', 'compiled_code'),
  ])

# Always use the following value for the Content-type header.
headers = { "Content-type": "application/x-www-form-urlencoded" }
conn = httplib.HTTPSConnection('closure-compiler.appspot.com')
conn.request('POST', '/compile', params, headers)
response = conn.getresponse()
data = response.read()
print data
conn.close()

注意:Windows 用户可能需要安装 Python 才能重现此示例。如需了解如何在 Windows 上安装和使用 Python,请参阅 Python Windows 常见问题解答

使用以下命令将代码发送到 Closure Compiler 服务:

$ python compile.py https://closure-compiler.appspot.com/closure/compiler/samples/tutorial2.js

Closure Compiler 服务从 https://closure-compiler.appspot.com/closure/compiler/samples/tutorial2.js 检索文件,并在响应中返回压缩的 JavaScript。

如需将多个输出文件编译到一个输出文件中,请添加多个 code_url 参数,如下例所示:

params = urllib.urlencode([
    # Multiple code_url parameters:
    ('code_url', 'http://yourserver.com/yourJsPart1.js'),
    ('code_url', 'http://yourserver.com/yourJsPart2.js'),
    ('compilation_level', 'WHITESPACE_ONLY'),
    ('output_format', 'text'),
    ('output_info', 'compiled_code'),
  ])

改善压缩效果

到目前为止,这些示例都使用 WHITESPACE_ONLYcompilation_level,它只是剥离了注释和空格。借助 SIMPLE_OPTIMIZATIONS 压缩级别,您可以实现更高的压缩率。如需使用 SIMPLE_OPTIMIZATIONS 压缩,请将 compilation_level 参数更改为 SIMPLE_OPTIMIZATIONS

params = urllib.urlencode([
    ('code_url', sys.argv[1]),
    ('compilation_level', 'SIMPLE_OPTIMIZATIONS'),  # <--- This parameter has a new value!
    ('output_format', 'text'),
    ('output_info', 'compiled_code'),
  ])

然后像之前一样运行脚本:

$ python compile.py https://closure-compiler.appspot.com/closure/compiler/samples/tutorial2.js

输出应如下所示:

var GLOBAL_document=document,$$PROP_appendChild="appendChild";function makeNoteDom(a,b,c){var d=GLOBAL_document.createElement("div");a=GLOBAL_document.createTextNode(a);d[$$PROP_appendChild](a);a=GLOBAL_document.createElement("div");b=GLOBAL_document.createTextNode(b);a[$$PROP_appendChild](b);b=GLOBAL_document.createElement("div");b[$$PROP_appendChild](d);b[$$PROP_appendChild](a);c[$$PROP_appendChild](b)}function makeNotes(a,b){for(var c=0;c<a.length;c++)makeNoteDom(a[c].title,a[c].content,b)}
function main(){var a=[{title:"Note 1",content:"Content of Note 1"},{title:"Note 2",content:"Content of Note 2"}],b=GLOBAL_document.getElementById("notes");makeNotes(a,b)}main();

此代码比源程序更难读,但更小。

代码有多小?

如果我们将请求参数中的 output_infocompiled_code 更改为 statistics,就可以确切地看到我们节省了多少空间:

Original Size: 1372
Compressed Size: 677
Compilation Time: 0

新的 JavaScript 小于原始 JavaScript 的一半。

Closure 编译器服务是如何缩小程序的?

在此情况下,Closure 编译器通过重命名局部变量来实现大小缩减。例如,原始文件包含以下代码行:

var headerElement = document.createElement('div');

Closure Compiler 将此语句更改为:

var d=document.createElement("div");

Closure 编译器会将函数 makeNoteDom 中的符号 headerElement 更改为 d,从而保留功能。但是,headerElement 的 13 个字符在显示时的三个位置已缩减为一个字符。这样可节省 36 个字符。

使用 SIMPLE_OPTIMIZATIONS 进行编译始终会保留语法上有效的 JavaScript 的功能,前提是代码不使用字符串名称(例如,使用 eval() 语句)访问局部变量。

后续操作

现在,您已经熟悉了 SIMPLE_OPTIMIZATIONS 和使用该服务的基本机制,下一步是了解 ADVANCED_OPTIMIZATIONS 编译级别。需要执行一些额外的步骤才能确保 JavaScript 在编译之前和之后的工作方式都相同,但会让 JavaScript 更小。请参阅高级编译和 Extern,了解 ADVANCED_OPTIMIZATIONS