joacub / uglify-js2
jsmin
This package is not auto-updated.
Last update: 2024-09-10 04:12:42 UTC
README
UglifyJS 是一个 JavaScript 解析器、压缩器、压缩器或美化工具包。
本页面记录了命令行实用程序。有关 API 和内部文档,请访问我的网站。还有 浏览器在线演示(适用于 Firefox、Chrome 和可能 Safari)。
安装
首先确保您已安装最新版本的 node.js(您可能需要在此步骤后重新启动计算机)。
从 NPM 作为命令行应用程序使用
npm install uglify-js -g
从 NPM 作为编程使用
npm install uglify-js
从 Git
git clone git://github.com/mishoo/UglifyJS2.git
cd UglifyJS2
npm link .
用法
uglifyjs [input files] [options]
UglifyJS2 可以接受多个输入文件。建议您首先传递输入文件,然后传递选项。UglifyJS 将按顺序解析输入文件并应用任何压缩选项。文件是在相同的全局范围内解析的,也就是说,从一个文件到在另一个文件中声明的变量/函数的引用将被正确匹配。
如果您想从 STDIN 读取,请传递一个短横线而不是输入文件。
可用的选项包括
--source-map Specify an output file where to generate source map.
[string]
--source-map-root The path to the original source to be included in the
source map. [string]
--source-map-url The path to the source map to be added in //#
sourceMappingURL. Defaults to the value passed with
--source-map. [string]
--in-source-map Input source map, useful if you're compressing JS that was
generated from some other original code.
--screw-ie8 Pass this flag if you don't care about full compliance
with Internet Explorer 6-8 quirks (by default UglifyJS
will try to be IE-proof). [boolean]
--expr Parse a single expression, rather than a program (for
parsing JSON) [boolean]
-p, --prefix Skip prefix for original filenames that appear in source
maps. For example -p 3 will drop 3 directories from file
names and ensure they are relative paths. You can also
specify -p relative, which will make UglifyJS figure out
itself the relative paths between original sources, the
source map and the output file. [string]
-o, --output Output file (default STDOUT).
-b, --beautify Beautify output/specify output options. [string]
-m, --mangle Mangle names/pass mangler options. [string]
-r, --reserved Reserved names to exclude from mangling.
-c, --compress Enable compressor/pass compressor options. Pass options
like -c hoist_vars=false,if_return=false. Use -c with no
argument to use the default compression options. [string]
-d, --define Global definitions [string]
-e, --enclose Embed everything in a big function, with a configurable
parameter/argument list. [string]
--comments Preserve copyright comments in the output. By default this
works like Google Closure, keeping JSDoc-style comments
that contain "@license" or "@preserve". You can optionally
pass one of the following arguments to this flag:
- "all" to keep all comments
- a valid JS regexp (needs to start with a slash) to keep
only comments that match.
Note that currently not *all* comments can be kept when
compression is on, because of dead code removal or
cascading statements into sequences. [string]
--stats Display operations run time on STDERR. [boolean]
--acorn Use Acorn for parsing. [boolean]
--spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
[boolean]
--self Build itself (UglifyJS2) as a library (implies
--wrap=UglifyJS --export-all) [boolean]
--wrap Embed everything in a big function, making the “exports”
and “global” variables available. You need to pass an
argument to this option to specify the name that your
module will take when included in, say, a browser.
[string]
--export-all Only used when --wrap, this tells UglifyJS to add code to
automatically export all globals. [boolean]
--lint Display some scope warnings [boolean]
-v, --verbose Verbose [boolean]
-V, --version Print version number and exit. [boolean]
指定 --output
(《-o》)以声明输出文件。否则,输出将发送到 STDOUT。
源映射选项
UglifyJS2 可以生成一个源映射文件,这对于调试压缩后的 JavaScript 非常有用。要获取源映射,请传递 --source-map output.js.map
(您希望源映射输出的文件的完整路径)。
此外,您可能还需要 --source-map-root
来传递可以找到原始文件的位置的 URL。如果您向 UglifyJS 传递输入文件的完整路径,则可以使用 --prefix
(《-p》)来指定在声明源映射中的文件时从路径前缀中删除的目录数量。
例如
uglifyjs /home/doe/work/foo/src/js/file1.js \
/home/doe/work/foo/src/js/file2.js \
-o foo.min.js \
--source-map foo.min.js.map \
--source-map-root http://foo.com/src \
-p 5 -c -m
上面的示例将压缩和混淆 file1.js
和 file2.js
,将输出文件放在 foo.min.js
中,源映射放在 foo.min.js.map
中。源映射将引用 http://foo.com/src/js/file1.js
和 http://foo.com/src/js/file2.js
(实际上它将 http://foo.com/src
列为源映射根,并将原始文件列为 js/file1.js
和 js/file2.js
)。
组合源映射
当您压缩由 CoffeeScript 等编译器输出的 JS 代码时,映射到 JS 代码不会很有用。相反,您可能希望映射回原始代码(即 CoffeeScript)。UglifyJS 有一个选项可以接受输入源映射。假设您有一个从 CoffeeScript → 编译后的 JS 的映射,UglifyJS 可以通过将编译后的 JS 中的每个标记映射到其原始位置来生成从 CoffeeScript → 压缩 JS 的映射。
要使用此功能,您需要传递 --in-source-map /path/to/input/source.map
。通常,输入源映射也应该指向包含生成的 JS 的文件,因此如果这是正确的,则可以省略命令行中的输入文件。
混淆器选项
要启用混淆器,您需要传递 --mangle
(《-m》)选项。以下(以逗号分隔)选项受支持:
-
sort
— 将较短的名字分配给最常用的变量。这可以在 gzip 之前的 jQuery 上节省几百字节,但输出在 gzip 之后会更大(并且似乎适用于我尝试过的其他库),因此默认情况下不启用。 -
toplevel
— 混淆顶级作用域中声明的名字(默认情况下禁用)。 -
eval
— 对在eval
或when
使用时可见的作用域中的名称进行混淆(默认禁用)。
当混淆已启用但您想防止某些名称被混淆时,可以使用--reserved
(-r
)声明这些名称——传递一个以逗号分隔的名称列表。例如
uglifyjs ... -m -r '$,require,exports'
防止require
、exports
和$
名称被更改。
压缩器选项
您需要传递--compress
(-c
)来启用压缩器。可选地,您可以传递一个以逗号分隔的选项列表。选项的形式为foo=bar
,或者只是foo
(后者意味着一个布尔选项,您希望将其设置为true
;它实际上是一个快捷方式,相当于foo=true
)。
sequences
— 使用逗号操作符连接连续的简单语句properties
— 使用点符号重写属性访问,例如foo["bar"] → foo.bar
dead_code
— 删除不可达的代码drop_debugger
— 删除debugger;
语句unsafe
(默认:false)— 应用“不安全”的转换(以下讨论)conditionals
— 对if
语句和条件表达式应用优化comparisons
— 对二进制节点应用某些优化,例如:!(a <= b) → a > b
(仅在unsafe
时),尝试否定二进制节点,例如:a = !b && !c && !d && !e → a=!(b||c||d||e)
等。evaluate
— 尝试评估常量表达式booleans
— 对布尔上下文进行各种优化,例如:!!a ? b : c → a ? b : c
loops
— 当我们可以静态确定条件时,对do
、while
和for
循环进行优化unused
— 删除未引用的函数和变量hoist_funs
— 提升函数声明hoist_vars
(默认:false)— 提升变量声明(默认为false
,因为这似乎会增加输出的大小)if_return
— 对if/return和if/continue进行优化join_vars
— 连接连续的var
语句cascade
— 对序列进行的小型优化,将x, x
转换为x
,将x = something(), x
转换为x = something()
warnings
— 在删除不可达代码或未使用声明等时显示警告negate_iife
— 否定立即调用的函数表达式(其中返回值被丢弃),以避免代码生成器插入的括号。
unsafe
选项
它启用了某些转换,这些转换可能会在特定的情况下破坏代码逻辑,但对于大多数代码来说应该是安全的。您可能想在自己的代码上尝试它,它应该会减少最小化的大小。当此标志打开时,会发生以下情况
new Array(1, 2, 3)
或Array(1, 2, 3)
→[1, 2, 3 ]
new Object()
→{}
String(exp)
或exp.toString()
→"" + exp
new Object/RegExp/Function/Error/Array (...)
→ 我们丢弃new
typeof foo == "undefined"
→foo === void 0
void 0
→"undefined"
(如果作用域中有一个名为“undefined”的变量;我们这样做是因为变量名将被混淆,通常简化为单个字符)。
条件编译
您可以使用--define
(-d
)切换来声明全局变量,UglifyJS将假设这些变量是常量(除非在作用域中定义)。例如,如果您传递--define DEBUG=false
,那么,结合代码删除,UglifyJS将从输出中删除以下内容
if (DEBUG) { console.log("debug stuff"); }
UglifyJS将警告条件始终为假,并关于删除不可达代码;目前没有选项可以仅关闭此特定警告,您可以通过传递warnings=false
来关闭所有警告。
另一种方法是,在单独的文件中将全局变量声明为常量,并将其包含到构建中。例如,您可以有build/defines.js
文件,内容如下
const DEBUG = false; const PRODUCTION = true; // etc.
并像这样构建您的代码
uglifyjs build/defines.js js/foo.js js/bar.js... -c
UglifyJS 会注意到常量,因为它们不能被改变,所以它会将它们的引用评估为它们的实际值,并像往常一样删除不可达的代码。这种方法的潜在缺点是构建将包含 const
声明。
美化器选项
默认情况下,代码生成器会尝试输出尽可能短的代码。如果您想要美化后的输出,请传递 --beautify
(-b
)。您还可以传递其他参数来控制代码输出
beautify
(默认true
) -- 是否实际上美化输出。传递-b
将此设置为 true,但您可能需要在生成压缩代码时也传递-b
,以指定其他参数,因此您可以使用-b beautify=false
来覆盖它。indent-level
(默认 4)indent-start
(默认 0) -- 在所有行前加上那么多空格quote-keys
(默认false
) -- 将所有字面量对象中的所有键用引号括起来space-colon
(默认true
) -- 在冒号后插入一个空格ascii-only
(默认false
) -- 在字符串和正则表达式中转义 Unicode 字符inline-script
(默认false
) -- 在字符串中转义</script
出现的斜杠width
(默认 80) -- 只有在美化开启时才生效,指定美化工具将尝试遵守的(指示性)行宽。它指的是行文本的宽度(不包括缩进)。目前效果不佳,但它确实使得 UglifyJS 生成的代码更易于阅读。max-line-len
(默认 32000) -- 最大行长度(对于压缩代码)bracketize
(默认false
) -- 总是在if
、for
、do
、while
或with
语句中插入括号,即使它们的主体只有一个语句。semicolons
(默认true
) -- 用分号分隔语句。如果您传递false
,则尽可能使用换行符而不是分号,这将导致压缩代码的输出更易于阅读(gzip 之前的尺寸可能更小;gzip 之后的尺寸显著更大)。
保留版权声明或其他注释
您可以通过传递 --comments
来保留输出中的某些注释。默认情况下,它将保留包含 "@preserve"、"@license" 或 "@cc_on"(IE 的条件编译)的 JSDoc 风格的注释。您可以通过传递 --comments all
来保留所有注释,或传递一个有效的 JavaScript 正则表达式来仅保留与该正则表达式匹配的注释。例如,--comments '/foo|bar/'
将仅保留包含 "foo" 或 "bar" 的注释。
但是请注意,可能存在注释丢失的情况。例如
function f() { /** @preserve Foo Bar */ function g() { // this function is never called } return something(); }
尽管它有 "@preserve",但注释将丢失,因为内部函数 g
(注释附加到的 AST 节点)被压缩器丢弃,因为没有被引用。
最安全的注释是放置在顶层节点上的注释(或需要保留在输出中的其他信息)。
SpiderMonkey AST 的支持
UglifyJS2 有它自己的抽象语法树格式;由于 实际原因,我们无法轻松地将其内部转换为使用 SpiderMonkey AST。然而,UglifyJS 现在有了一个转换器,可以导入 SpiderMonkey AST。
例如 Acorn 是一个超级快的解析器,它生成 SpiderMonkey AST。它有一个小的 CLI 工具,可以解析一个文件并在标准输出上以 JSON 格式输出 AST。要使用 UglifyJS 来混淆和压缩这些
acorn file.js | uglifyjs --spidermonkey -m -c
《--spidermonkey》选项告诉UglifyJS所有输入文件都不是JavaScript,而是用JSON描述的SpiderMonkey AST(抽象语法树)中的JS代码。因此,在这种情况下我们不会使用自己的解析器,而是直接将AST转换为内部AST。
使用Acorn进行解析
更有趣的是,我添加了《--acorn》选项,它将使用Acorn进行所有解析。如果您传递此选项,UglifyJS将调用《require("acorn")》。
Acorn真的很快(例如,在650K代码上,250ms而不是380ms),但将Acorn生成的SpiderMonkey树转换为另一种格式需要额外的150ms,所以总共还是要比仅使用UglifyJS自己的解析器慢一点。
API参考
假设通过NPM安装,您可以在应用程序中以这种方式加载UglifyJS
var UglifyJS = require("uglify-js");
它导出了很多名称,但在这里我会讨论解析、混淆和压缩代码块所需的基本内容。顺序是(1)解析,(2)压缩,(3)混淆,(4)生成输出代码。
简单方法
有一个顶层函数,它结合了所有步骤。如果您不需要额外的自定义,您可能想使用《minify》。示例
var result = UglifyJS.minify("/path/to/file.js"); console.log(result.code); // minified output // if you need to pass code instead of file name var result = UglifyJS.minify("var b = function () {};", {fromString: true});
您也可以压缩多个文件
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]); console.log(result.code);
生成源映射
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], { outSourceMap: "out.js.map" }); console.log(result.code); // minified output console.log(result.map);
请注意,源映射不会保存在文件中,它只是作为《result.map》返回。传递给《outSourceMap》的值仅用于设置源映射中的《file》属性(请参阅规范)。
您还可以指定要包含在源映射中的sourceRoot属性
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], { outSourceMap: "out.js.map", sourceRoot: "http://example.com/src" });
如果您正在压缩编译后的JavaScript并且有相应的源映射,您可以使用《inSourceMap》参数
var result = UglifyJS.minify("compiled.js", { inSourceMap: "compiled.js.map", outSourceMap: "minified.js.map" }); // same as before, it returns `code` and `map`
《inSourceMap》仅在使用《outSourceMap》时使用(否则没有意义)。
其他选项
-
warnings
(默认值为false
)——传递true
以显示压缩器警告。 -
fromString
(默认值为false
)——如果传递true
,则可以传递JavaScript源代码,而不是文件名。 -
mangle
——传递false
以跳过名称混淆。 -
output
(默认值为null
)——如果您想指定额外的输出选项,请传递一个对象。默认值是为了最佳压缩而优化的。 -
compress
(默认值为{}
)——传递false
以完全跳过压缩。传递一个对象以指定自定义压缩器选项。
我们可以在《UglifyJS.minify》中添加更多选项——如果您需要额外的功能,请提出建议!
困难方法
以下是更详细的API信息,以防《minify》函数对您来说太简单了。
解析器
var toplevel_ast = UglifyJS.parse(code, options);
《options》是可选的,如果存在,它必须是一个对象。以下属性是可用的
strict
——禁用自动分号插入和在数组和对象中支持尾随逗号filename
——包含此代码的文件名称toplevel
——一个顶层节点(如前一个《parse》调用返回的)
后两个选项在您想要压缩多个文件并得到一个单独的输出文件以及适当的源映射时很有用。我们的CLI工具就是这样做的
var toplevel = null; files.forEach(function(file){ var code = fs.readFileSync(file); toplevel = UglifyJS.parse(code, { filename: file, toplevel: toplevel }); });
之后,我们在《toplevel》中有一个包含所有文件的大AST(抽象语法树),每个标记都有关于其来源的正确信息。
作用域信息
UglifyJS 包含一个作用域分析器,您需要在压缩或混淆之前手动调用。基本上,它会将 AST 中的各种节点增强为有关名称定义位置、名称引用次数、是否为全局变量、函数是否使用 eval
或 with
语句等信息。我将在其他地方讨论这个问题,现在重要的是要知道,在处理树之前,您需要调用以下内容:
toplevel.figure_out_scope()
压缩
例如这样
var compressor = UglifyJS.Compressor(options); var compressed_ast = toplevel.transform(compressor);
options
可以省略。可用的选项在上文“压缩器选项”中讨论。默认值应该会在大多数脚本中导致最佳压缩。
压缩器是破坏性的,因此不要依赖于 toplevel
保持原始树。
混淆
压缩后,再次调用 figure_out_scope
是一个好主意(因为压缩器可能会删除未使用的变量/不可达的代码,这可能会更改标识符的数量或它们的位置)。可选地,您可以在 Gzip 后调用一个技巧(在不可混淆的词中计数字符频率)。示例
compressed_ast.figure_out_scope(); compressed_ast.compute_char_frequency(); compressed_ast.mangle_names();
生成输出
AST 节点有一个 print
方法,它接受一个输出流。本质上,要生成代码,您这样做
var stream = UglifyJS.OutputStream(options); compressed_ast.print(stream); var code = stream.toString(); // this is your minified code
或者,为了方便,您可以这样做
var code = compressed_ast.print_to_string(options);
通常,options
是可选的。输出流接受很多选项,其中大部分在上文“美化器选项”部分中已有文档说明。我们在这里关注的是 source_map
和 comments
。
在输出中保留注释
为了在输出中保留某些注释,您需要传递 comments
选项。传递一个正则表达式或一个函数。如果您传递一个正则表达式,只有那些主体与正则表达式匹配的注释将被保留。请注意,主体意味着不包括初始的 //
或 /*
。如果您传递一个函数,它将为树中的每个注释调用,并接收两个参数:注释附加到的节点和注释标记本身。
注释标记具有以下属性
type
:对于单行注释为 "comment1" 或对于多行注释为 "comment2"。value
:注释主体。pos
和endpos
:在原始代码中出现此注释的起始/结束位置(基于零的索引)。line
和col
:在原始代码中此注释出现的行和列。file
—— 原始文件的文件名。nlb
—— 如果在原始代码中此注释之前有换行符,或者此注释包含换行符,则为 true。
您的函数应返回 true
以保留注释,否则返回一个假值。
生成源映射
在调用 print
时需要传递 source_map
参数。它需要是一个 SourceMap
对象(它是在 source-map 库之上的一个薄包装)。
示例
var source_map = UglifyJS.SourceMap(source_map_options); var stream = UglifyJS.OutputStream({ ... source_map: source_map }); compressed_ast.print(stream); var code = stream.toString(); var map = source_map.toString(); // json output for your source map
source_map_options
(可选)可以包含以下属性
file
:此映射引用的 JavaScript 输出文件的名称。root
:sourceRoot
属性(参见 规范)。orig
:原始的 "original source map",当您压缩生成的 JS 并希望将压缩后的输出映射回原始代码时很有用。它可以是一个简单的 JSON 字符串,也可以是一个包含原始源映射的 JSON 对象。