原文連接-uglify-vs-babel-minify-vs-terser-a-mini-battle-royalecss
縮小(也是最小化)是從 解釋的編程語言 或 標記語言 的源代碼中刪除全部沒必要要的字符而不改變其功能的過程。這些沒必要要的字符一般包括前端
讓咱們試着經過一個例子來理解這一點。下面的代碼顯示了一個示例 JavaScript
代碼,用於建立數組並使用前20個整數值對其進行初始化:node
var array = []; for(var i = 0 ; i < 20 ; i ++){ array [ i ] = i ; }
複製代碼
如今,讓咱們嘗試手動縮小這些代碼。下面的示例顯示了咱們如何只使用一行代碼來實現相同的功能:react
for(var a = [ i = 0 ]; ++ i < 20 ; a [ i ] = i );
複製代碼
首先,咱們減小了 array
變量的名稱(array to a)
,而後咱們將它移動到循環初始化構造中。咱們還將第 3 行的數組初始化移動到循環中。結果,字符數和文件大小顯着減小。webpack
如今咱們瞭解縮小是什麼,很容易猜到咱們爲何這麼作。因爲縮小縮小了源代碼的大小,所以其在網絡上的傳輸變得更有效。git
這對於 Web 和 移動應用程序 尤爲有用,其中前端向後端發出http請求以獲取文件,用戶數據等資源。對於至關大的應用程序,如Instagram
或Facebook
,前端一般安裝在用戶的設備上,然後端和數據庫做爲本地服務器或雲中的多個實例存在。github
在諸如加載照片的典型用戶操做中,前端向後端發出http
請求,然後端又向數據庫實例發出請求以獲取所請求的資源。這涉及經過網絡傳輸數據,而且該過程的效率與正在傳輸的數據的大小成正比。這正是縮小有用的地方。web
在上一節中,咱們看到了如何手動縮小一個簡單的代碼。但這對於巨大的代碼庫來講實際上不是一個可擴展的解決方案。多年來已經有各類構建工具來縮小 JavaScript
代碼。接下來讓咱們來看看最受歡迎的一些解決方案:數據庫
UglifyJS 的目標是縮小和壓縮代碼。讓咱們繼續使用如下命令安裝它:npm
npm install uglify - js - g
複製代碼
如今讓咱們嘗試在JavaScript
模塊上運行uglify
。爲此,我編寫了一個示例模塊,代碼以下:sample.js
var print = "Hello world! Let's minify everything because, less is more"
var apple = [1,2,3,4,5]
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
for(let element in apple) {
console.log(print)
console.log(element)
}
複製代碼
此代碼基本上在循環內打印字符串和數組。咱們屢次複製for()
循環以增長文件的大小,這樣咱們就能夠更好地看到Uglify
的效果。
文件大小爲1.2KB
咱們能夠看到,Uglify
有不少選項,其中大部分都是不言自明的。那麼,讓咱們繼續嘗試其中幾個:
文件大小爲944B,並經過重複打印字符串和數組值來執行
咱們在文件上使用了 -c
(compress)和-m
(mangle)選項並對其進行了修改。文件大小減小到944B,減小了大約22%。如今,讓咱們看看文件內容,看看它是如何經過uglification
更改的:-c -m sample.js
var print="Hello world! Let's minify everything because, less is more",apple=[1,2,3,4,5];for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log (print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);for(i=0;i<5;i++)console.log(print),console.log(apple[i]);
複製代碼
從上面的示例中,咱們能夠看到輸出的內容具備相同的代碼,沒有任何空格和換行符。
爲了進一步瞭解Uglify
的效果,讓咱們用原型函數編寫一個示例JS代碼:
// comments
function sample(helloworld) {
this.hello = helloworld
}
sample.prototype.printvar = function()
{
console.log(this.hello)
}
var hello = "hello world"
var s = new sample(hello)
s.printvar()
複製代碼
如今,讓咱們編譯一下這段代碼:
Uglified 代碼是131B。
請注意,我使用了Uglify
(附帶)選項。此選項將全部內容嵌入到一個大函數中,具備可配置的參數和值。爲了理解這意味着什麼,讓咱們看一下輸出的內容:-e
!function(){function o(o){this.hello=o}o.prototype.printvar=function(){console.log(this.hello)};new o("hello world").printvar()}();
複製代碼
在uglified
輸出中,咱們能夠看到函數名稱 sample
已經消失,而且被替換爲 o
。全部代碼都包含在一個大函數中,這會以可讀性爲代價進一步減少代碼的大小。
如今,讓咱們看看另外一個流行的縮小器:babel-minify
。
babel- minify,之前稱爲Babili,是一個實驗項目,試圖使用Babel
的工具鏈(用於編譯)以相似的方式作一些事情:縮小。它目前是 0.x,官方存儲庫不建議在生產中使用它。
當咱們已經擁有Uglify
時,爲何咱們須要這個工具?若是你注意到前面的例子,我沒有使用最新版ECMAScript
的語法。這是由於Uglify
還不支持它 - 可是babel-minify
能夠。
這是由於它只是一組Babel
插件,Babel
已經瞭解瞭解析器Babylon
的新語法。此外,當能夠僅定位支持較新 ES 功能的瀏覽器時,您的代碼大小能夠更小,由於您沒必要進行轉換而後縮小它。
在 babel-minify
以前,咱們將運行Babel
來轉換ES6,而後運行Uglify來縮小代碼。經過babel-minify
,這個兩步過程基本上變成了一個步驟。
babel-minify
是ES2015 +知道的,由於它是使用 Babel
工具鏈構建的。它被寫成一組Babel
插件,帶有 babel-preset-minify 的消耗品。咱們來看一個例子。
讓咱們使用如下命令在本地安裝 Babel
和 Babel
預設以轉換ES6:
npm install - save - dev @babel / core @babel / cli
npm install - save - dev babel - plugin - transform - es2015 - classes
複製代碼
如今,讓咱們用ES6語法編寫一個:sample.js
//ES6 Syntax
class sample {
constructor(helloworld) {
this.hello = helloworld
}
printvar() {
console.log(this.hello)
}
}
var hello = "hello world"
var s = new sample(hello)
s.printvar()
複製代碼
讓咱們使用如下命令來轉換此代碼:
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
//ES6 Syntax
let sample = function () {
function sample(helloworld) {
_classCallCheck(this, sample);
this.hello = helloworld;
}
_createClass(sample, [{
key: "printvar",
value: function printvar() {
console.log(this.hello);
}
}]);
return sample;
}();
var hello = "hello world";
var s = new sample(hello);
s.printvar();
複製代碼
已轉換代碼的內容以下所示:
如您所見,ES6 類語法已轉換爲常規函數語法。如今,讓咱們在這個內容上運行 Uglify
來縮小它:
uglifyjs sample-transpiled.js -c -m -e -o sample-transpiled-uglified.js
複製代碼
如今,壓縮的內容以下所示:
function _classCallCheck(e,l){if(!(e instanceof l))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,l){for(var n=0;n<l.length;n++){var r=l[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function _createClass(e,l,n){return l&&_defineProperties(e.prototype,l),n&&_defineProperties(e,n),e}let sample=function(){function e(l){_classCallCheck(this,e),this.hello=l}return _createClass(e,[{key:"printvar",value:function(){console.log(this.hello)}}]),e}();var hello="hello world",s=new sample(hello);s.printvar();
複製代碼
若是咱們比較這些文件大小,sample.js is 227B
, sample-transpiled.js is 1KB
, and sample-transpiled-uglified.js is 609B
. 很明顯的發現,這不是最佳的處理過程,由於它會致使文件大小的增長。 爲了解決這個問題,引入了 babel-minify
。 如今,讓咱們安裝 babel-minify
並嘗試轉換和縮小代碼。
npm install babel-minify --save-dev
複製代碼
接下來咱們使用下面的命令來壓縮相同的 sample.js
minify sample.js > sample-babili.js
複製代碼
咱們能夠看下執行事後的輸出內容:
class sample{constructor(l){this.hello=l}printvar(){console.log(this.hello)}}var hello="hello world",s=new sample(hello);s.printvar();
複製代碼
這個文件的大小是 135B, 將近壓縮了40%,是一種更好的壓縮代碼的方式。它直接提升了經過網絡傳輸它的效率,並能夠在不少瀏覽器上面運行,由於 Babel
能夠轉換代碼。還有各類插件可使用。
Terser 是 ES6+
的 JavaScript
解析器 和 mangler/compressor
工具包, 它多是最有效的。Terser 建議您搭配 Rollup 打包使用,這樣會產生更小的代碼。
Rollup
是一個相似於 webpack
的 模塊打包器,它是爲了儘量高效地構建 JavaScript
庫的平面可分發版而建立的,利用了ES2015模塊的巧妙設計。ES6 模塊最終仍是要由瀏覽器原生實現,但當前 Rollup 可使你提早體驗。
雖然 Rollup
是一個不錯的選擇,但若是你使用的是 webpack> v4
,默認狀況下會使用 Terser
。 能夠經過切換布爾變量來啓用 Terser
,以下所示:
module.exports = {
//...
optimization: {
minimize: false
}
};
複製代碼
安裝 Terser
npm install terser -g
複製代碼
Terser
命令行具備如下語法:
terser [input files] [options]
複製代碼
Terser
能夠採用多個輸入文件。 建議您先傳遞輸入文件,而後傳遞選項。 Terser
將按順序解析輸入文件並應用任何壓縮選項。
這些文件在同一個全局範圍內解析 - 也就是說,從文件到另外一個文件中聲明的某個變量/函數的引用將被正確匹配。 若是未指定輸入文件,則 Terser
將從STDIN
讀取。
若是您但願在輸入文件以前傳遞選項,請使用雙短劃線將二者分開以防止輸入文件用做選項參數:
terser --compress --mangle -- input.js
複製代碼
如今讓咱們嘗試運行咱們的sample.js代碼:
terser -c toplevel,sequences=false --mangle -- sample.js > sample-terser.js
複製代碼
如下是此輸出的內容
new class{constructor(l){this.hello=l}printvar(){console.log(this.hello)}}("hello world").printvar();
複製代碼
咱們能夠看到,到目前爲止,咱們所看到的全部工具中的輸出是迄今爲止最好的。 文件大小爲 102B,比原始 sample.js
大小減小了近 55%。 可使用 --help
選項找到 Terser
的其餘命令行選項。
Terser 命令行選項。 在全部選項中,咱們最感興趣的是 --compress
和 --mangle
,每一個選項都有本身的選項集。 --compress
和 --mangle
的選項使您能夠控制如何處理源代碼以生成縮小的輸出。
若是您注意到,咱們已經在第一個 Terser
示例中使用了 --compress
的頂層和序列選項。 例如,您能夠將 true
傳遞給 --compress
的drop_console
選項以從源代碼中刪除全部 console.*
函數,若是您不想破壞類名,則可使用 keep_classnames
選項。
有時,美化生成的輸出可能頗有用。 您可使用 --beautify
選項執行此操做。 許多構建工具使用Terser
- 在這裏找到它們。
讓咱們嘗試在源文件中使用 drop_console
選項來查看console.log()
函數是否被刪除:
terser --compress drop_console=true -- sample.js > sample-drop-console.js
複製代碼
如今,讓咱們看看源代碼,sample.js的內容:
//ES6 Syntax
class sample {
constructor(helloworld) {
this.hello = helloworld
}
printvar() {
console.log(this.hello)
}
}
var hello = "hello world"
var s = new sample(hello)
s.printvar()
複製代碼
而如今輸出,sample-drop-console.js:
new class{constructor(r){this.hello=r}printvar(){}}("hello world").printvar();
複製代碼
正如咱們所看到的,代碼被進一步破壞和壓縮,這個新文件的大小僅爲 79B。 與不使用 drop_console
選項時所看到的 55%相比,這減少了65%。 這樣,咱們能夠根據項目的要求使用選項來平衡可讀性和性能。
到目前爲止,咱們共研究了三種最流行的壓縮 js 和css 代碼的工具。Babel
存儲庫比較基準測試結果提供了這些統計信息,能夠幫助您爲項目選擇合適的壓縮工具。
經過上面的圖,咱們能夠看拿到,Terser
在基於 React
項目中表現最好。您還能夠在查看其餘 web 框架的表現結果。
在本節中,咱們將介紹爲 React
應用程序配置 minifier
的過程。 讓咱們在這個例子中使用Terser來縮小React應用程序。 爲了以簡化的方式實現這一目標,咱們使用 webpack
。 webpack
是一個工具鏈,用於將全部文件捆綁到一個名爲bundle.js
的文件中,該文件能夠高效加載。 讓咱們使用如下命令安裝 webpack
:
npm install webpack --save-dev
複製代碼
咱們還須要安裝一些Babel插件以便轉換代碼:
npm install babel-core babel-loader babel-preset-env babel-preset-react\babel-preset-stage-0 --save-dev
複製代碼
接下來,讓咱們安裝Terser插件:
npm install terser-webpack-plugin --save-dev
複製代碼
咱們還須要.svg
和.css
的 loader,因此咱們也要安裝它們:
npm install svg-inline-loader --save-dev
npm install css-loader --save-dev
複製代碼
在這個階段,咱們只須要配置 webpack.config.js
文件,具體配置能夠參考下面:
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
entry: "./src/index.js",
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options:{
presets: ['@babel/preset-react']
}
}
},
{
test: /\.svg$/,
loader: 'svg-inline-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
}]
},
optimization: {
minimizer: [new TerserPlugin()],
},
}
複製代碼
從上面的代碼中,咱們能夠看到webpack
的入口文件是src /
中的 index.js
,最終輸出將做爲bundle.js
存儲在dist /
目錄中。 優化字段將 TerserPlugin
拉入縮小過程。 如今,讓咱們運行webpack
來靜態構建咱們的應用程序以進行生產。
咱們能夠看到 webpack
在全部文件上運行 loader
和插件,並構建了一個大小爲 938KB 的bundle.js
,而咱們的整個應用程序比這大得多。 這是webpack
以及相關加載器和插件的真正威力。
最近推出了一些新的捆綁包。 其中,Rollup和Parcel愈來愈受歡迎。 任何捆綁工具的基礎配置和設置相似於 webpack
。 您能夠在此處找到 webpack
,Rollup
和Parcel
之間的性能比較, 連接在此。
最後一點,讓我經過展現npm趨勢的片斷來結束這篇文章。 咱們能夠看到,Terser
在過去六個月中得到了極大的人氣。 與其餘縮小工具相比,這能夠直接歸因於其更好的性能。
感謝您閱讀這篇文章。 我但願你能學習到壓縮代碼的一些知識和對一些長期的疑惑點可以獲得清晰地解決。