UglifyJS中文文件

語言: CN / TW / HK

UglifyJS是JavaScript解析器,縮小器,壓縮器和美化器工具箱。

由於webpack本身集成了UglifyJS外掛(webpack.optimize.UglifyJsPlugin),其命令webpack -p即表示呼叫UglifyJS來壓縮程式碼,還有不少webpack外掛如html-webpack-plugin也會預設使用UglifyJS。

與UglifyJS2相比API變動較大,簡化較多,文件也增加了不少示例。

由於webpack本身集成了UglifyJS外掛(webpack.optimize.UglifyJsPlugin),其命令webpack -p即表示呼叫UglifyJS來壓縮程式碼,還有不少webpack外掛如html-webpack-plugin也會預設使用UglifyJS。因此我們其實經常要用到它,但UglifyJS本身配置較複雜/選項繁多,又沒有中文文件,使用起來如墜雲霧。鑑於此特翻譯此文,謬誤甚多,敬請斧正。

詞典:

parse       解釋
compress    壓縮
mangle      混淆
beautify    美化
minify      最小化
CLI         命令列工具
sourcemap   編譯後代碼對原始碼的對映,用於網頁除錯
AST         抽象語法樹
name        名字,包括變數名、函式名、屬性名
toplevel    頂層作用域
unreachable 不可達程式碼
option      選項/配置
STDIN       標準輸入,指在命令列中直接輸入
STDOUT      標準輸出
STDERR      標準錯誤輸出
side effects函式副作用,即函式除了返回外還產生別的作用,比如改了全域性變數
shebang     釋伴(#!)

以下為正文:

UglifyJS 3

UglifyJS 是一個js 直譯器、最小化器、壓縮器、美化器工具集(parser, minifier, compressor or beautifier toolkit)。

注意:

  • uglify-js@3 的API  CLI已簡化,不再向後相容 [email protected].

  • UglifyJS 2.x 文件在這裡.

  • uglify-js 只支援 ECMAScript 5 (ES5).

  • 假如希望壓縮 ES2015+ (ES6+)程式碼,應該使用 uglify-es這個npm 包。

安裝

首先確認一直你已經安裝了最新的node.js(裝完後或許需要重啟一下電腦)

用NPM安裝CLI:

npm install uglify-js -g

用NPM下載給程式使用:

npm install uglify-js

CLI使用

Command line usage

uglifyjs [input files] [options]

UglifyJS可以輸入多檔案。建議你先寫輸入檔案,再傳選項。UglifyJS會根據壓縮選項,把檔案放在佇列中依次解釋。所有檔案都會在同一個全域性域中,假如一個檔案中的變數、方法被另一檔案引用,UglifyJS會合理地匹配。

假如沒有指定檔案,UglifyJS會讀取輸入字串(STDIN)。

如果你想要把選項寫在檔名的前面,那要在二者之前加上雙橫線,防止檔名被當成了選項:

uglifyjs --compress --mangle -- input.js

CLI選項:

Command line options

  -h, --help                  列出使用指南。
                              `--help options` 獲取可用選項的詳情。
  -V, --version               列印版本號。
  -p, --parse <options>       指定解析器配置選項:
                              `acorn`  使用 Acorn 來解析。
                              `bare_returns`  允許在函式外return。
                                              在壓縮CommonJS模組或`.user.js `引擎呼叫被同步執行函式包裹的使用者指令碼 時會用到。
                              `expression`  不是解析檔案,二是解析一段表示式 (例如解析JSON).
                              `spidermonkey`  輸入檔案是 SpiderMonkey
                                              AST 格式 (JSON).
  -c, --compress [options]    啟用壓縮(true/false)/指定壓縮配置:
                              `pure_funcs`  傳一個函式名的列表,當這些函式返回值沒被利用時,該函式會被安全移除。
  -m, --mangle [options]       啟用混淆(true/false)/指定混淆配置:
                              `reserved`  不被混淆的名字列表。
  --mangle-props [options]    混淆屬性/指定壓縮配置:
                              `builtins`  混淆那些與標準JS全域性變數重複的名字。
                              `debug`  新增debug字首和字尾。
                              `domprops`  混淆那些魚DOM屬性名重複的名字。
                              `keep_quoted`  只混淆沒括起來的屬性名。
                              
                              `regex`  只混淆匹配(該正則)的名字。
                              `reserved`  不需要混淆的名字的列表(即保留)。
  -b, --beautify [options]    是否美化輸出(true/false)/指定輸出配置:
                              `beautify`  預設是啟用.
                              `preamble`  預設的輸出檔案頭部。你可以插入一段註釋,比如版權資訊。它不會被解析,但sourcemap會因此調整。
                              `quote_style`  括號型別:
                                              0 - auto自動
                                              1 - single單引號
                                              2 - double雙引號
                                              3 - original跟隨原碼
                              `wrap_iife`  把立即執行函式括起來。注意:你或許應禁用壓縮配置中的`negate_iife`選項。 

 -o, --output <file>         輸出檔案路徑 (預設 STDOUT). 指定 `ast``spidermonkey`的話分別是輸出UglifyJS或SpiderMonkey AST。
    --comments [filter]         保留版權註釋。預設像Google Closure那樣,保留包含"@license""@preserve"這樣JSDoc風格的註釋。你可以傳以下的引數:
                                - "all" 保留全部註釋
                                - 一個合適的正則,如 `/foo/``/^!/`,保留匹配到的註釋。 
                                注意,在啟用壓縮時,因為死程式碼被移除或壓縮宣告為一行,並非*所有*的註釋都會被保留。
    --config-file <file>        從此JSON檔案讀取 `minify()` 配置。
    -d, --define <expr>[=value] 定義全域性變數。
    --ie8                       支援IE8。
                                等同於在`minify()``compress``mangle``output`配置設定`ie8: true`。UglifyJS不會預設相容IE8。
    --keep-fnames               不要混淆、幹掉的函式的名字。當代碼依賴Function.prototype.name時有用。
    --name-cache <file>         用來儲存混淆map的檔案。
    --self                      把UglifyJS本身也構建成一個依賴包
                                (等同於`--wrap UglifyJS`)
    --source-map [options]      啟用 source map(true/false)/指定sourcemap配置:
                                `base` 根路徑,用於計算輸入檔案的相對路徑。
                                `content`  輸入sourcemap。假如的你要編譯的JS是另外的原始碼編譯出來的。
                                假如該sourcemap包含在js內,請指定"inline"`filename`  輸出檔案的名字或位置。
                                `includeSources`  如果你要在sourcemap中加上原始檔的內容作sourcesContent屬性,就傳這個引數吧。
                                `root`  此路徑中的原始碼編譯後會產生sourcemap.
                                `url`   如果指定此值,會新增sourcemap相對路徑在`//#sourceMappingURL`中。
    --timings                   在STDERR顯示操作執行時間。
    --toplevel                  壓縮/混淆在最高作用域中宣告的變數名。
    --verbose                   列印診斷資訊。
    --warn                      列印警告資訊。
    --wrap <name>               把所有程式碼包裹在一個大函式中。讓“exports”和“global”變數有效。
                                你需要傳一個引數來指定此模組的名字,以便瀏覽器引用。         

指定--output (-o)來明確輸出檔案,否則將在終端輸出(STDOUT)

CLI sourcemap選項

CLI source map options

UglifyJS可以生成一份sourcemap檔案,這非常有利於你除錯壓縮後的JS程式碼。傳--source-map --output output.js來獲取sorcemap檔案(sorcemap會生成為output.js.map)。

額外選項:

  • --source-map filename=<NAME> 指定sourcemap名字。

  • --source-map root=<URL> 傳一個原始檔的路徑。否則UglifyJS將假定已經用了HTTPX-SourceMap,並將省略//#sourceMappingURL=指示。

  • --source-map url=<URL> 指定生成sourcemap的路徑。

例如:

    uglifyjs js/file1.js js/file2.js \
             -o foo.min.js -c -m \
             --source-map root="http://foo.com/src",url=foo.min.js.map

上述配置會壓縮和混淆file1.jsfile2.js,輸出檔案foo.min.js 和sourcemapfoo.min.js.map,sourcemap會建立http://foo.com/src/js/file1.js
http://foo.com/src/js/file2.js的對映。(實際上,sourcemap根目錄是http://foo.com/src,所以相當於原始檔路徑是js/file1.jsjs/file2.js

關聯sourcemap

Composed source map

假如你的JS程式碼是用其他編譯器(例如coffeescript)生成的,那麼對映到JS程式碼就沒什麼用了,你肯定希望對映到CoffeeScript原始碼。UglifyJS有一個選項可以輸入sourcemap,假如你有一個從CoffeeScript → 編譯後JS的map的話,UglifyJS可以生成一個從CoffeeScript->壓縮後JS的map對映到原始碼位置。

你可以傳入 --source-map content="/path/to/input/source.map"或來嘗試此特性,如果sourcemap包含在js內,則寫 --source-map content=inline

CLI混淆選項

CLI mangle options

你需要傳入--mangle (-m)來使啟用混淆功能。支援以下選項(用逗號隔開):

  • toplevel — 混淆在最高作用域中宣告的變數名(預設disabled)

  • eval - 混淆在eval  with作用域出現的變數名(預設disabled)

當啟用混淆功能時,如果你希望保留一些名字不被混淆,你可以用--mangle reserved 宣告一些名字(用逗號隔開)。例如:

uglifyjs ... -m reserved=[$,require,exports]'

這樣能防止require, exports $被混淆改變。

CLI混淆屬性名 (--mangle-props)

CLI mangling property names (--mangle-props)

警告:這能會搞崩你的程式碼。混淆屬性名跟混淆變數名不一樣,是相互獨立的。傳入--mangle-props會混淆物件所有可見的屬性名,除了DOM屬性名和JS內建的類名。例如:

// example.js
var x = {
    baz_: 0,
    foo_: 1,
    calc: function() {
        return this.foo_ + this.baz_;
    }
};
x.bar_ = 2;
x["baz_"] = 3;
console.log(x.calc());

混淆所有屬性(除了JS內建的):

$ uglifyjs example.js -c -m --mangle-props
var x={o:0,_:1,l:function(){return this._+this.o}};x.t=2,x.o=3,console.log(x.l());

混淆除了 reserved (保留)外的所有屬性:

$ uglifyjs example.js -c -m --mangle-props reserved=[foo_,bar_]
var x={o:0,foo_:1,_:function(){return this.foo_+this.o}};x.bar_=2,x.o=3,console.log(x._());

混淆匹配regex(正則)的屬性:

$ uglifyjs example.js -c -m --mangle-props regex=/_$/
var x={o:0,_:1,calc:function(){return this._+this.o}};x.l=2,x.o=3,console.log(x.calc());

混用多個混淆屬性選項:

$ uglifyjs example.js -c -m --mangle-props regex=/_$/,reserved=[bar_]
var x={o:0,_:1,calc:function(){return this._+this.o}};x.bar_=2,x.o=3,console.log(x.calc());

為了混淆正常使用,我們預設避免混淆標準JS內建的名字(--mangle-props builtins可以強制混淆)。

tools/domprops.json 裡有一個預設的排除名單,包括絕大部分標準JS和多種瀏覽器中的DOM屬性名。傳入--mangle-props domprops 可以讓此名單失效。

可以用正則表示式來定義該混淆的屬性名。例如--mangle-props regex=/^_/,只混淆下劃線開頭的屬性。

當你壓縮多個檔案時,為了保證讓它們最終能同時工作,我們要讓他們中同樣的屬性名混淆成相同的結果。傳入`--name-cache
filename.json`,UglifyJS會維護一個共同的對映供他們複用。這個json一開始應該是空的,例如:

$ rm -f /tmp/cache.json  # start fresh
$ uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
$ uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js

這樣part1.js  part2.js會知曉對方混淆的屬性名。

假如你把所有檔案壓縮成同一個檔案,那就不需要啟用名字快取了。

混淆沒括起來的名字(--mangle-props keep_quoted)

Mangling unquoted names (--mangle-props keep_quoted)

使用括號屬性名 (o["foo"])以保留屬性名(foo)。這會讓整個指令碼中其餘此屬性的引用(o.foo)也不被混淆。例如:

// stuff.js
var o = {
    "foo": 1,
    bar: 3
};
o.foo += o.bar;
console.log(o.foo);
$ uglifyjs stuff.js --mangle-props keep_quoted -c -m
var o={foo:1,o:3};o.foo+=o.o,console.log(o.foo);

除錯屬性名混淆

Debugging property name mangling

為了混淆屬性時不至於完全分不清,你可以傳入--mangle-props debug來除錯。例如o.foo會被混淆成o._$foo$_。這讓原始碼量大、屬性被混淆時也可以debug,可以看清混淆會把哪些屬性搞亂。

$ uglifyjs stuff.js --mangle-props debug -c -m
var o={_$foo$_:1,_$bar$_:3};o._$foo$_+=o._$bar$_,console.log(o._$foo$_);

你可以用--mangle-props-debug=XYZ來傳入自定義字尾。讓o.foo 混淆成 o._$foo$XYZ_, 你可以在每次編譯是都改變一下,來辨清屬性名怎麼被混淆的。一個小技巧,你可以每次編譯時傳隨機數來模仿混淆操作(例如你更新了指令碼,有了新的屬性名),這有助於識別混淆時的出錯。

API參考

API Reference

假如是通過NPM安裝的,你可以在你的應用中這樣載入UglifyJS:

var UglifyJS = require("uglify-js");

這輸出一個高階函式minify(code, options),它能根據配置,實現多種最小化(即壓縮、混淆等)。 minify()預設啟用壓縮和混淆選項。例子:

var code = "function add(first, second) { return first + second; }";
var result = UglifyJS.minify(code);
console.log(result.error); // runtime error, or `undefined` if no error
console.log(result.code);  // minified output: function add(n,d){return n+d}

你可以通過一個物件(key為檔名,value為程式碼)來同時最小化多個檔案:

var code = {
    "file1.js": "function add(first, second) { return first + second; }",
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var result = UglifyJS.minify(code);
console.log(result.code);
// function add(d,n){return d+n}console.log(add(3,7));

toplevel選項例子:

var code = {
    "file1.js": "function add(first, second) { return first + second; }",
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = { toplevel: true };
var result = UglifyJS.minify(code, options);
console.log(result.code);
// console.log(3+7);

nameCache 選項例子:

var options = {
    mangle: {
        toplevel: true,
    },
    nameCache: {}
};
var result1 = UglifyJS.minify({
    "file1.js": "function add(first, second) { return first + second; }"
}, options);
var result2 = UglifyJS.minify({
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
}, options);
console.log(result1.code);
// function n(n,r){return n+r}
console.log(result2.code);
// console.log(n(3,7));

你可以像下面這樣把名字快取儲存在檔案中:

var cacheFileName = "/tmp/cache.json";
var options = {
    mangle: {
        properties: true,
    },
    nameCache: JSON.parse(fs.readFileSync(cacheFileName, "utf8"))
};
fs.writeFileSync("part1.js", UglifyJS.minify({
    "file1.js": fs.readFileSync("file1.js", "utf8"),
    "file2.js": fs.readFileSync("file2.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync("part2.js", UglifyJS.minify({
    "file3.js": fs.readFileSync("file3.js", "utf8"),
    "file4.js": fs.readFileSync("file4.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync(cacheFileName, JSON.stringify(options.nameCache), "utf8");

綜合使用多種minify()選項的例子:

var code = {
    "file1.js": "function add(first, second) { return first + second; }",
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = {
    toplevel: true,
    compress: {
        global_defs: {
            "@console.log": "alert"
        },
        passes: 2
    },
    output: {
        beautify: false,
        preamble: "/* uglified */"
    }
};
var result = UglifyJS.minify(code, options);
console.log(result.code);
// /* uglified */
// alert(10);"

生成警告提示:

var code = "function f(){ var u; return 2 + 3; }";
var options = { warnings: true };
var result = UglifyJS.minify(code, options);
console.log(result.error);    // runtime error, `undefined` in this case
console.log(result.warnings); // [ 'Dropping unused variable u [0:1,18]' ]
console.log(result.code);     // function f(){return 5}

生成錯誤提示:

var result = UglifyJS.minify({"foo.js" : "if (0) else console.log(1);"});
console.log(JSON.stringify(result.error));
// {"message":"Unexpected token: keyword (else)","filename":"foo.js","line":1,"col":7,"pos":7}

Note: unlike [email protected], the 3.x API does not throw errors. To
achieve a similar effect one could do the following:

var result = UglifyJS.minify(code, options);
if (result.error) throw result.error;

最小化選項

Minify options

  • warnings (default false) — 傳 true的話,會在result.warnings中返回壓縮過程的警告。傳 "verbose"獲得更詳細的警告。

  • parse (default {}) — 如果你要指定額外的解析配置parse options,傳配置物件。

  • compress (default {}) — 傳false就完全跳過壓縮。傳一個物件來自定義 壓縮配置compress options。

  • mangle (default true) — 傳 false就跳過混淆名字。傳物件來指定混淆配置mangle options (詳情如下).

    • mangle.properties (default false) — 傳一個物件來自定義混淆屬性配置mangle property options.

  • output (default null) — 要自定義就傳個物件來指定額外的 輸出配置output options. 預設是壓縮到最優化。

  • sourceMap (default false) - 傳一個物件來自定義
    sourcemap配置source map options.

  • toplevel (default false) - 如果你要混淆(和幹掉沒引用的)最高作用域中的變數和函式名,就傳true

  • nameCache (default null) - 如果你要快取 minify()多處呼叫的經混淆的變數名、屬性名,就傳一個空物件{}或先前用過的nameCache物件。
    注意:這是個可讀/可寫屬性。minify()會讀取這個物件的nameCache狀態,並在最小化過程中更新,以便保留和供使用者在外部使用。

  • ie8 (default false) - 傳 true 來支援 IE8.

最小化配置的結構

Minify options structure

{
    warnings: false,
    parse: {
        // parse options
    },
    compress: {
        // compress options
    },
    mangle: {
        // mangle options

        properties: {
            // mangle property options
        }
    },
    output: {
        // output options
    },
    sourceMap: {
        // source map options
    },
    nameCache: null, // or specify a name cache object
    toplevel: false,
    ie8: false,
}

sourcemap配置

Source map options

這樣生成sourcemap:

var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
    sourceMap: {
        filename: "out.js",
        url: "out.js.map"
    }
});
console.log(result.code); // minified output
console.log(result.map);  // source map

要注意,此時sourcemap並不會儲存為一份檔案,它只會返回在result.map中。
sourceMap.url 傳入的值只用來在result.code中設定//# sourceMappingURL=out.js.map filename 的值只用來在sourcemap檔案中設定 file屬性(詳情看 規範)。

你可以把sourceMap.url設為true ,這樣sourcemap會加在程式碼末尾。

你也可以指定sourcemap中的原始檔根目錄(sourceRoot)屬性:

var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
    sourceMap: {
        root: "http://example.com/src",
        url: "out.js.map"
    }
});

如果你要壓縮從其他檔案編譯得來的帶一份sourcemap的JS檔案,你可以用sourceMap.content引數:

var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
    sourceMap: {
        content: "content from compiled.js.map",
        url: "minified.js.map"
    }
});
// same as before, it returns `code` and `map`

如果你要用 X-SourceMap 請求頭,你可以忽略 sourceMap.url

解析配置

Parse options

  • bare_returns (default false) -- 支援在頂級作用域中 return 宣告。

  • html5_comments (default true)

  • shebang (default true) -- 支援在第一行用 #!command

壓縮配置

Compress options

  • sequences(default: true) -- 連續宣告變數,用逗號隔開來。可以設定為正整數來指定連續宣告的最大長度。如果設為true 表示預設200個,設為false0則禁用。 sequences至少要是2,1的話等同於true(即200)。預設的sequences設定有極小几率會導致壓縮很慢,所以推薦設定成20或以下。

  • properties -- 用.來重寫屬性引用,例如foo["bar"] → foo.bar

  • dead_code -- 移除沒被引用的程式碼

  • drop_debugger -- 移除 debugger;

  • unsafe (default: false) -- 使用 "unsafe"轉換 (下面詳述)

  • unsafe_comps (default: false) -- 保留<  <=不被換成 >  >=。假如某些運算物件是用get valueOfobject得出的時候,轉換可能會不安全,可能會引起運算物件的改變。此選項只有當 comparisonsunsafe_comps 都設為true時才會啟用。

  • unsafe_Func (default: false) -- 當 Function(args, code)args  code都是字串時,壓縮並混淆。

  • unsafe_math (default: false) -- 優化數字表達式,例如2 * x * 3 變成 6 * x, 可能會導致不精確的浮點數結果。

  • unsafe_proto (default: false) -- 把Array.prototype.slice.call(a) 優化成 [].slice.call(a)

  • unsafe_regexp (default: false) -- 如果RegExp 的值是常量,替換成變數。

  • conditionals -- 優化if等判斷以及條件選擇

  • comparisons -- 把結果必然的運算優化成二元運算,例如!(a <= b) → a > b (只有設定了 unsafe_comps時才生效);儘量轉成否運算。例如 a = !b && !c && !d && !e → a=!(b||c||d||e)

  • evaluate -- 嘗試計算常量表達式

  • booleans -- 優化布林運算,例如 !!a? b : c → a ? b : c

  • typeofs -- 預設 true. 轉換 typeof foo == "undefined"  foo === void 0. 注意:如果要適配IE10或以下,由於已知的問題,推薦設成false 

  • loops -- 當dowhile  for迴圈的判斷條件可以確定是,對其進行優化。

  • unused -- 幹掉沒有被引用的函式和變數。(除非設定"keep_assign",否則變數的簡單直接賦值也不算被引用。)

  • toplevel -- 幹掉頂層作用域中沒有被引用的函式 ("funcs")和/或變數("vars") (預設是false , true 的話即函式變數都幹掉)

  • top_retain -- 當設了unused時,保留頂層作用域中的某些函式變數。(可以寫成陣列,用逗號隔開,也可以用正則或函式. 參考toplevel)

  • hoist_funs -- 提升函式宣告

  • hoist_vars (default: false) -- 提升 var 宣告 (預設是false,因為那會加大檔案的size)

  • if_return -- 優化 if/return 和 if/continue

  • inline -- 包裹簡單函式。

  • join_vars -- 合併連續 var 宣告

  • cascade -- 弱弱地優化一下連續宣告, 將 x, x 轉成 xx = something(), x 轉成 x = something()

  • collapse_vars -- 當 var  const 單獨使用時儘量合併

  • reduce_vars -- 優化某些變數實際上是按常量值來賦值、使用的情況。

  • warnings -- 當刪除沒有用處的程式碼時,顯示警告

  • negate_iife -- 當立即執行函式(IIFE)的返回值沒用時,取消之。避免程式碼生成器會插入括號。

  • pure_getters -- 預設是 false. 如果你傳入true,UglifyJS會假設物件屬性的引用(例如foo.bar  foo["bar"])沒有函式副作用。

  • pure_funcs -- 預設 null. 你可以傳入一個名字的陣列,UglifyJS會假設這些函式沒有函式副作用。警告:假如名字在作用域中重新定義,不會再次檢測。例如var q = Math.floor(a/b),假如變數q沒有被引用,UglifyJS會幹掉它,但 Math.floor(a/b)會被保留,沒有人知道它是幹嘛的。你可以設定pure_funcs: [ 'Math.floor' ] ,這樣該函式會被認為沒有函式副作用,這樣整個宣告會被廢棄。在目前的執行情況下,會增加開銷(壓縮會變慢)。

  • drop_console -- 預設 false. 傳true的話會幹掉console.*函式。如果你要幹掉特定的函式比如console.info ,又想刪掉後保留其引數中的副作用,那用pure_funcs來處理吧。

  • expression -- 預設 false。傳true來保留終端語句中沒有"return"的完成值。例如在bookmarklets。

  • keep_fargs -- 預設true。阻止壓縮器幹掉那些沒有用到的函式引數。你需要它來保護某些依賴Function.length的函式。

  • keep_fnames -- 預設 false。傳 true來防止壓縮器幹掉函式名。對那些依賴Function.prototype.name的函式很有用。延展閱讀:keep_fnames 混淆選項.

  • passes -- 預設 1。執行壓縮的次數。在某些情況下,用一個大於1的數字引數可以進一步壓縮程式碼大小。注意:數字越大壓縮耗時越長。

  • keep_infinity -- 預設 false。傳true以防止壓縮時把1/0轉成Infinity,那可能會在chrome上有效能問題。

  • side_effects -- 預設 true. 傳false禁用丟棄純函式。如果一個函式被呼叫前有一段/*@__PURE__*/ or /*#__PURE__*/ 註釋,該函式會被標註為純函式。例如 /*@__PURE__*/foo();

混淆配置

Mangle options

  • reserved (default [])。 傳一個不需要混淆的名字的陣列。 Example: ["foo", "bar"].

    • toplevel (default false)。混淆那些定義在頂層作用域的名字(預設禁用)。ß

    • keep_fnames(default false)。傳true的話就不混淆函式名。對那些依賴Function.prototype.name的程式碼有用。延展閱讀:keep_fnames 壓縮配置.

    • eval (default false)。混淆那些在with或eval中出現的名字。

// test.js
var globalVar;
function funcName(firstLongName, anotherLongName) {
    var myVariable = firstLongName +  anotherLongName;
}
var code = fs.readFileSync("test.js", "utf8");

UglifyJS.minify(code).code;
// 'function funcName(a,n){}var globalVar;'

UglifyJS.minify(code, { mangle: { reserved: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;'

UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;'

混淆屬性的配置

Mangle properties options

  • reserved (default: []) -- 不混淆在reserved 數組裡的屬性名.

  • regex (default: null) -— 傳一個正則,只混淆匹配該正則的屬性名。

  • keep_quoted (default: false) -— 只混淆不在括號內的屬性名.

  • debug (default: false) -— 用原名字來組成混淆後的名字.
    傳空字串"" 來啟用,或者非空字串作為debu字尾。(例如"abc", foo.bar=>foo.barabc)

  • builtins (default: false) -- 傳 true的話,允許混淆內建的DOM屬性名。不推薦使用。

輸出配置

Output options

程式碼生成器預設會盡量輸出最簡短的程式碼。假如你要美化一下輸出程式碼,可以傳--beautify (-b)。你也可以傳更多的引數來控制輸出程式碼:

  • ascii_only (default false) -- 忽略字串和正則(導致非ascii字元失效)中的Unicode字元。

  • beautify (default true) -- 是否美化輸出程式碼。傳-b的話就是設成true。假如你想生成最小化的程式碼同時又要用其他設定來美化程式碼,你可以設-b beautify=false

  • bracketize (default false) -- 永遠在if, for,do, while, with後面加上大括號,即使迴圈體只有一句。

  • comments (default false) -- 傳 true  "all"保留全部註釋,傳 "some"保留部分,傳正則 (例如 /^!/) 或者函式也行。

  • indent_level (default 4) 縮排格數

  • indent_start (default 0) -- 每行前面加幾個空格

  • inline_script (default false) -- 避免字串中出現</script中的斜槓

  • keep_quoted_props (default false) -- 如果啟用,會保留物件屬性名的引號。

  • max_line_len (default 32000) -- 最大行寬(壓縮後的程式碼)

  • space-colon (default true) -- 在冒號後面加空格

  • preamble (default null) -- 如果要傳的話,必須是字串。它會被加在輸出文件的前面。sourcemap會隨之調整。例如可以用來插入版權資訊。

  • preserve_line (default false) -- 傳 true 就保留空行,但只在beautify 設為false時有效。ß

  • quote_keys (default false) -- 傳true的話會在物件所有的鍵加上括號

  • quote_style (default 0) -- 影響字串的括號格式(也會影響屬性名和指令)。

  • 0 -- 傾向使用雙引號,字串裡還有引號的話就是單引號。

  • 1 -- 永遠單引號

  • 2 -- 永遠雙引號

  • 3 -- 永遠是本來的引號

  • semicolons (default true) -- 用分號分開多個宣告。如果你傳false,則總會另起一行,增強輸出檔案的可讀性。(gzip前體積更小,gzip後稍大一點點)

  • shebang (default true) -- 保留開頭的 shebang #! (bash 指令碼)

  • width (default 80) -- 僅在美化時生效,設定一個行寬讓美化器儘量實現。這會影響行中文字的數量(不包括縮排)。當前本功能實現得不是非常好,但依然讓美化後的程式碼可讀性大大增強。

  • wrap_iife (default false) --傳true的話,把立即執行函式括起來。 更多詳情看這裡
    #640

綜合應用

Miscellaneous

保留版權告示或其他註釋

你可以傳入--comments讓輸出檔案中保留某些註釋。預設時會保留JSDoc-style的註釋(包含"@preserve","@license" 或 "@cc_on"(為IE所編譯))。你可以傳入--comments all來保留全部註釋,或者傳一個合法的正則來保留那些匹配到的註釋。例如--comments /^!/會保留/*! Copyright Notice */這樣的註釋。

注意,無論如何,總會有些註釋在某些情況下會丟失。例如:

function f() {
    /** @preserve Foo Bar */
    function g() {
        // this function is never called
    }
    return something();
}

即使裡面帶有"@preserve",註釋依然會被丟棄。因為內部的函式g(註釋所依附的抽象語法樹節點)沒有被引用、會被壓縮器幹掉。

書寫版權資訊(或其他需要在輸出檔案中保留的資訊)的最安全位置是全域性節點。

unsafe`compress`配置

The unsafe compress option

在某些刻意營造的案例中,啟用某些轉換有可能會打斷程式碼的邏輯,但絕大部分情況下是安全的。你可能會想嘗試一下,因為這畢竟會減少檔案體積。以下是某些例子:

  • 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

  • void 0  undefined (假如作用域中有一個變數名叫"undefined";我們這麼做是因為變數名會被混淆成單字元)

編譯條件語句

Conditional compilation

Uglify會假設全域性變數都是常量(不管是否在區域性域中定義了),你可以用--define (-d)來實現定義全域性變數。例如你傳--define DEBUG=false,UglifyJS會在輸出中幹掉下面程式碼:

if (DEBUG) {
    console.log("debug stuff");
}

你可以像--define env.DEBUG=false這樣寫巢狀的常量。

在幹掉那些永否的條件語句以及不可達程式碼時,UglifyJS會給出警告。現在沒有選項可以禁用此特性,但你可以設定 warnings=false 來禁掉所有警告。

另一個定義全域性常量的方法是,在一個獨立的文件中定義,再引入到構建中。例如你有一個這樣的build/defines.js

const DEBUG = false;
const PRODUCTION = true;
// 等等

這樣構建你的程式碼:

uglifyjs build/defines.js js/foo.js js/bar.js... -c

UglifyJS會注意到這些常量。因為它們無法改變,所以它們會被認為是沒被引用而被照樣幹掉。如果你用const宣告,構建後還會被保留。如果你的執行環境低於ES6、不支援const,請用var宣告加上reduce_vars設定(預設啟用)來實現。

編譯條件語句API

你也可以通過程式API來設定編譯配置。其中有差別的是一個壓縮器屬性global_defs

var result = UglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
    compress: {
        dead_code: true,
        global_defs: {
            DEBUG: false
        }
    }
});

global_defs"@"字首的表示式,UglifyJS才會替換成語句表示式:

UglifyJS.minify("alert('hello');", {
    compress: {
        global_defs: {
            "@alert": "console.log"
        }
    }
}).code;
// returns: 'console.log("hello");'

否則會替換成字串:

UglifyJS.minify("alert('hello');", {
    compress: {
        global_defs: {
            "alert": "console.log"
        }
    }
}).code;
// returns: '"console.log"("hello");'

使用minify()獲得原生UglifyJS ast

Using native Uglify AST with minify()

// 例子: 只解析程式碼,獲得原生Uglify AST

var result = UglifyJS.minify(code, {
    parse: {},
    compress: false,
    mangle: false,
    output: {
        ast: true,
        code: false  // optional - faster if false
    }
});

// result.ast 即是原生 Uglify AST
// 例子: 輸入原生 Uglify AST,接著把它壓縮並混淆,生成程式碼和原生ast

var result = UglifyJS.minify(ast, {
    compress: {},
    mangle: {},
    output: {
        ast: true,
        code: true  // 可選,false更快
    }
});

// result.ast 是原生 Uglify AST
// result.code 是字串格式的最小化後的程式碼

使用 Uglify AST

Working with Uglify AST

可以通過TreeWalkerTreeTransformer分別橫截(?transversal)和轉換原生AST。

ESTree/SpiderMonkey AST

UglifyJS有自己的抽象語法樹格式;為了某些現實的原因
我們無法在內部輕易地改成使用SpiderMonkey AST。但UglifyJS現在有了一個可以輸入SpiderMonkeyAST的轉換器。
例如Acorn ,這是一個超級快的生成SpiderMonkey AST的直譯器。它帶有一個實用的迷你CLI,能解釋一個檔案、把AST轉存為JSON並標準輸出。可以這樣用UglifyJS來壓縮混淆:

acorn file.js | uglifyjs --spidermonkey -m -c

-p --spidermonkey選項能讓UglifyJS知道輸入檔案並非JavaScript,而是SpiderMonkey AST生成的JSON程式碼。這事我們不用自己的直譯器,只把AST轉成我們內部AST。

使用 Acorn 來解釋程式碼

Use Acorn for parsing

更有趣的是,我們加了 -p --acorn選項來使用Acorn解釋所有程式碼。如果你傳入這個選項,UglifyJS會require("acorn")

Acorn確實非常快(650k程式碼原來要380ms,現在只需250ms),但轉換Acorn產生的SpiderMonkey樹會額外花費150ms。所以總共比UglifyJS自己的直譯器還要多花一點時間。

Uglify Fast Minify Mode

很少人知道,對大多數js程式碼而言,其實移除空格和混淆符號已經佔了減少程式碼體積之中到的95%--不必細緻地轉換。簡單地禁用壓縮compress能加快UglifyJS的構建速度三四倍。我們可以比較一下
butternut和只使用混淆mangle的模式的Uglify的壓縮速度與gzip大小:
butternut:

d3.js minify size gzip size minify time (seconds)
original 451,131 108,733 -
[email protected] mangle=false, compress=false 316,600 85,245 0.70
[email protected] mangle=true, compress=false 220,216 72,730 1.13
[email protected] 217,568 72,738 1.41
[email protected] mangle=true, compress=true 212,511 71,560 3.36
[email protected] 210,713 72,140 12.64

在CLI中,這樣啟用快速最小化模式:

uglifyjs file.js -m

API這樣用:

 
UglifyJS.minify(code, { compress: false, mangle: true });
分享到: