Closure Compiler 、
YUI Compressorなどで圧縮されたスクリプトを見ると、
.prototype
、
.length
、
offsetParent
の重複したキーの無限の文字列を見ることができます。 例としてjQuery UI Sortableプラグインを使用して、それらを取り除きましょう。 すぐにgzipを超えることはできないと言いますが、手元にない場合や使用できない場合(たとえば、
10K Apartのコンペティションなど)、この圧縮手法は非常に便利です。
jquery.ui.sortable.jsファイルバージョン1.8.2をjQuery UIバンドルで最も印象的なものの1つとしてみましょう。 ソースコードの重量は38.5 KBで、Closure Compilerで圧縮されています-23.1 KB、gzipで-5.71 KB。 画面を乱雑にしないために、いくつかの特徴的な方法を選択し
、これが別個のプラグインであると
想像します 。
_mouseCapture()
が好きだった:
- / *!
- *ソースコード
- * /
- ( 関数 ($){
- var
- _mouseCapture = function ( event 、overrideHandle){
- if ( this .reverting){
- falseを 返し ます 。
- }
- if ( this .options.disabled || this .options.type == 'static' )はfalseを 返し ます 。
- //最初にアイテムデータを更新する必要があります
- this ._refreshItems( event );
- //クリックされたノード(またはその親の1つ)がthis.itemsの実際のアイテムであるかどうかを調べる
- var currentItem = null 、self = this 、nodes = $( event .target).parents()。each( function (){
- if ($ .data( this 、 ' sortable -item' )== self){
- currentItem = $( this );
- falseを 返し ます 。
- }
- });
- if ($ .data( event .target、 ' sortable -item' )== self)currentItem = $( event .target);
- if (!currentItem) return false ;
- if ( this .options.handle && !! overrideHandle){
- var validHandle = false ;
- $( this .options.handle、currentItem).find( "*" ).andSelf()。each( function (){ if ( this == event .target)validHandle = true ;});
- if (!validHandle) return false ;
- }
- this .currentItem = currentItem;
- this ._removeCurrentsFromItems();
- trueを 返し ます 。
- };
- })(jQuery);
混乱しないように、それをテストプラグインと呼び、Sortable全体を元のプラグインと呼びます。 テストプラグインの重量は正確に1 KBで、圧縮形式で576バイト、gzipで391バイトです。
/*! <br> * <br> * , 576 <br> */ <br>( function (d){ var _mouseCapture= function (a,b){ if ( this .reverting) return false ; if ( this .options.disabled|| this .options.type== "static" ) return false ; this ._refreshItems(a); var c= null ,e= this ;d(a.target).parents().each( function (){ if (d.data( this , "sortable-item" )==e){c=d( this ); return false }}); if (d.data(a.target, "sortable-item" )==e)c=d(a.target); if (!c) return false ; if ( this .options.handle&&!b){ var f= false ;d( this .options.handle,c).find( "*" ).andSelf().each( function (){ if ( this ==a.target)f= true }); if (!f) return false } this .currentItem=c; this ._removeCurrentsFromItems(); return true }})(jQuery);
this
→ self
まず、
this
ユビキタスで非圧縮性の表現を取り除きます。 元のプラグインでは、587回以上使用されています(2.3 KBまたはコードの6%)。 関数の先頭にローカル変数
self
を追加します。
var <br> self = this ;<br>
ここで、
this
関数の本体のすべてを
self
に置き換えて、圧縮結果を確認します。
それは:
this .currentItem=e; this ._removeCurrentsFromItems();
次のようになりました:
d.currentItem=e;d._removeCurrentsFromItems();
テストプラグイン全体:
/*! <br> * this > self <br> * YUI Compressor, 552 <br> */ <br>( function (b){ var a= function (f,g){ var d= this ; if (d.reverting){ return false } if (d.options.disabled||d.options.type== "static" ){ return false }d._refreshItems(f); var e= null ,c=b(f.target).parents().each( function (){ if (b.data( this , "sortable-item" )==d){e=b( this ); return false }}); if (b.data(f.target, "sortable-item" )==d){e=b(f.target)} if (!e){ return false } if (d.options.handle&&!g){ var h= false ;b(d.options.handle,e).find( "*" ).andSelf().each( function (){ if ( this ==f.target){h= true }}); if (!h){ return false }}d.currentItem=e;d._removeCurrentsFromItems(); return true }})(jQuery);
悪くない! ところで、19行目のこのメソッドには、作成された関数にコンテキストを渡すための
self = this
宣言がすでにまったく同じでしたが、何らかの理由で最適化には使用されませんでした。 ここでは、このアナウンスメントを最初に示しただけで、追加できる別の関数では、少なくとも4文字が関数で使用されている場合(さらに70文字あります)、余分な数文字が返済されます。
キーと文字列
それでは、残りのコードを注意深く見てみましょう。 繰り返し式の
options
、
target
、
data
、
"sortable-item"
などがはっきりと見えます。 それらのほとんどはオブジェクトまたは文字列のプロパティの名前であり、一部は元のプラグインで50〜60回検出されます。 コンプレッサーのうち、クロージャーコンパイラーのみがアドバンストモードでこれらの要素を圧縮(名前を短いものに変更)できますが、ここではほとんどすべてのプロパティをその場所で観察します。 最も可能性が高いのは、ライブラリをコンパイルする際に、外部の広範なリスト、非圧縮性の名前が与えられたことです。 最適化の方法は言語構文自体によって促されるため、この状況を修正したいという自然な欲求があります。 頻繁に使用されるキーをローカル変数にしましょう:
var <br> a = {<br> options: {<br> visible: true ,<br> mess: 'hi' <br> }<br> };
1:
if (a.options.visible) {<br> alert(a.options.mess);<br> a.options.visible = false ;<br>}
2:
var <br> o = 'options' ,<br> v = 'visible' ;<br> if (a[o][v]) {<br> alert(a[o].mess);<br> a[o][v] = false ;<br>}
2番目のオプションでは、
visible
を2回ではなく1回だけ書き込み、optionを3回ではなく1回書き込む必要がありました。 40キロバイトのプラグインの規模、さらにはアプリケーション全体の規模では、これによりコードサイズが明確に向上します(ただし、わかりやすさは向上しません)。
任意の言語で小さなスクリプトを記述して、コード内の式の頻度を決定し、それらを変数で自動的に置き換えることができます。 たとえば、次のような正規表現によって:
/((\')|(\")|\.)\b([a-z_][\w-]+\w)\b(?(2)\')(?(3)\")/i
。 ソースプラグインの場合、175個の置換が取得されました。テストプラグインの場合は15個です。
- / *!
- *これを置き換える> self
- *キーと文字列の置き換え
- * /
- ( 関数 ($){
- var
- _reverting = ' reverting ' 、 // 3
- _options = 'options' 、 // 51
- _disabled = 'disabled' 、 // 4
- _type = 'type' 、 // 2
- _static = 'static' 、 // 2
- __refreshItems = '_refreshItems' 、 // 2
- _target = 'target' 、 // 4
- _parents = 'parents' 、 // 2
- _each = 'each' 、 // 5
- _data = 'data' 、 // 5
- _sortable_item = ' sortable -item' 、 // 6
- _handle = 'handle' 、 // 2
- _find = 'find' 、 // 2
- _currentItem = 'currentItem' 、 // 52
- FALSE =!1、 // 30
- TRUE =!0、 // 11
- _mouseCapture = function ( event 、overrideHandle){
- var
- self = this ;
- if (self [_reverting]){
- FALSEを返します。
- }
- if (self [_options] [_ disabled] || self [_options] [_ type] == _static)FALSEを返します。
- self [__ refreshItems]( event );
- var currentItem = null 、nodes = $( event [_target])[_ parent]()[_ each]( function (){
- if ($ [_ data]( this 、_sortable_item)== self){
- currentItem = $( this );
- FALSEを返します。
- }
- });
- if ($ [_ data]( event [_target]、_sortable_item)== self)currentItem = $( event [_target]);
- if (!currentItem)はFALSEを返します。
- if (self [_options] [_ handle] &&!overrideHandle){
- var validHandle = FALSE;
- $(self [_options] [_ handle]、currentItem)[_ find]( "*" ).andSelf()[_ each]( function (){ if ( this == event [_target])validHandle = TRUE;});
- if (!validHandle)はFALSEを返します。
- }
- self [_currentItem] = currentItem;
- self._removeCurrentsFromItems();
- TRUEを返します。
- };
- })(jQuery);
変数の宣言は煩雑に見えますが、少なくとも元のプラグインのすべてのメソッドに共通することを忘れないでくださいが、理論的には、アプリケーション全体の名前の一般的な宣言を行うことができます。 「アクティビティのフィールド」が大きいほど、名前の圧縮はより効果的に機能します。 それらのそれぞれは、モジュールの先頭で1回だけ示され、その後はどこでも1文字または2文字の表現に置き換えられます。 圧縮された形式では、テストプラグインは見栄えがよくなります。
- / *!
- *これを置き換える> self
- *キーと文字列の置き換え
- * YUIコンプレッサー、395 B + JS美人
- * /
- ( 関数 (c){
- var b = function (v、w){
- var t = this ;
- if (t [e]){
- 戻る
- }
- if (t [m] [k] || t [m] [r] == f){
- 戻る
- }
- t [n](v);
- var u = null 、
- s = c(v [i])[g]()[q]( function (){
- if (c [l]( this 、j)== t){
- u = c( これ );
- 戻る
- }
- });
- if (c [l](v [i]、j)== t){
- u = c(v [i])
- }
- if (!u){
- 戻る
- }
- if (t [m] [d] &&!w){
- var x = o;
- c(t [m] [d]、u)[a]( "*" ).andSelf()[q]( function (){
- if ( this == v [i]){
- x = h
- }
- });
- if (!x){
- 戻る
- }
- }
- t [p] = u;
- t._removeCurrentsFromItems();
- hを返す
- }
- })(jQuery);
document
、 window
など。
これらのグローバルオブジェクトは、ローカル変数に割り当てられた場合も完全に圧縮されます。 テストプラグインでは、
document
と
window
使用されないため、一時的に隣接する
_mouseDrag()
メソッドを使用します。
( function () {<br><br> if ( event .pageY - $( document ).scrollTop() < o.scrollSensitivity)<br> scrolled = $( document ).scrollTop($( document ).scrollTop() - o.scrollSpeed);<br> else if ($(window).height() - ( event .pageY - $( document ).scrollTop()) < o.scrollSensitivity)<br> scrolled = $( document ).scrollTop($( document ).scrollTop() + o.scrollSpeed);<br> <br> if ( event .pageX - $( document ).scrollLeft() < o.scrollSensitivity)<br> scrolled = $( document ).scrollLeft($( document ).scrollLeft() - o.scrollSpeed);<br> else if ($(window).width() - ( event .pageX - $( document ).scrollLeft()) < o.scrollSensitivity)<br> scrolled = $( document ).scrollLeft($( document ).scrollLeft() + o.scrollSpeed);<br><br>})();
このような画像は、スクリプトでよく見られます。 このようなコードは、ローカル変数がないため、スペースと不要な文字を削除することによってのみ圧縮されます。 次に、置換を使用した圧縮を見てみましょう。
( function () {<br> var g = document ,<br> f = window,<br> e = "scrollTop" ,<br> c = "scrollLeft" ,<br> b = "scrollSpeed" ,<br> d = "scrollSensitivity" ,<br> h = "pageY" ,<br> a = "pageX" ;<br><br> if ( event [h] - $(g)[e]() < o[d]) {<br> scrolled = $(g)[e]($(g)[e]() - o[b])<br> } else {<br> if ($(f).height() - ( event [h] - $(g)[e]()) < o[d]) {<br> scrolled = $(g)[e]($(g)[e]() + o[b])<br> }<br> }<br><br> if ( event [a] - $(g)[c]() < o[d]) {<br> scrolled = $(g)[c]($(g)[c]() - o[b])<br> } else {<br> if ($(f).width() - ( event [a] - $(g)[c]()) < o[d]) {<br> scrolled = $(g)[c]($(g)[c]() + o[b])<br> }<br> }<br><br>})();
これはオプティマイザーのABCですが、何らかの理由で、これらの単純なトリックは、適切な場所にある大規模なライブラリでもほとんど使用されません。 開発者がプロジェクトレベルでの速記変数の使用について一定の合意に達した場合、圧縮されたjQueryの重さは70 KBではなく、たとえば50になる可能性があります。
前の例では、jQueryラッパー内のドキュメントオブジェクトへの参照を使用して変数を作成することをお勧めします。
var <br> doc = document ,<br> $doc = $(doc);
叙情的な余談
jQueryで動的に作成された関数では、ほとんどの場合(はい)ラッパーに要素が必要であり、次のようにする必要があります。
$( 'li' ).each( function (index, item) {<br> var <br> $item = $(item)<br> /* do something with $item */ <br>});
このような構造は、ライブラリのソースにもあります。 そして、要素を持つjQueryオブジェクトが3番目のオプションパラメータにすぐに渡されるとしたら、どれほど簡単になるでしょう。
$( 'li' ).each( function (index, item, $item) {<br> /* do something with $item */ <br>});
美人!
結果
ただし、テストプラグインに戻ります。 最適化後、395バイトと576の重さが増え始め、31.4%を獲得しました。
/*! <br> * this > self <br> * <br> * YUI Compressor, 395 <br> */ <br>( function (c){ var b= function (v,w){ var t= this ; if (t[e]){ return o} if (t[m][k]||t[m][r]==f){ return o}t[n](v); var u= null ,s=c(v[i])[g]()[q]( function (){ if (c[l]( this ,j)==t){u=c( this ); return o}}); if (c[l](v[i],j)==t){u=c(v[i])} if (!u){ return o} if (t[m][d]&&!w){ var x=o;c(t[m][d],u)[a]( "*" ).andSelf()[q]( function (){ if ( this ==v[i]){x=h}}); if (!x){ return o}}t[p]=u;t._removeCurrentsFromItems(); return h}})(jQuery);
オリジナルのプラグインのスケールでは、変数宣言が追加されているため、当然ながら画像は多少異なります。 すべての操作の結果、圧縮された形式のプラグインの重量は正確に18 KBになり始め、ゲインは5.3 KB(21.5%)になりました。 しかし、逆にgzipは0.7 KB(13%)重いため、この手法は、何らかの理由でgzpが利用できない場合に最適です。 この方法で圧縮されたファイルは、gzipをサポートしていないSafari 2などの古いブラウザーにも提供できます。
ソースプラグインのサイズ(バイト単位):
| ソースコード | これ→自己 | プロパティキャッシング |
圧縮なし | 39,495 | 40 241 | 41,148 |
YUI / CC | 23,656 | 22 185 | 18 496 |
gzip | 5 851 | 5,950 | 6 504 |
UPD:グローバルオブジェクトの圧縮の例を追加しました。