12

大多數人可能都熟悉Google Analytics提供的這個小跟蹤代碼。如何縮小Google Analytics之類的JavaScript?

<script> 
(
    function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 
    } 
)(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 

ga('create', 'UA-00000000-0', 'auto'); 
ga('send', 'pageview'); 
</script> 

有趣的部分是,這個小小的摘錄包含形成單詞isogram的參數。該腳本還使用參數來聲明變量以從最終文件大小中刪除一些位。可以說,編寫代碼(?)時不會使用這種模式,所以我的問題是,谷歌如何縮小其代碼,並且這些技術是否也可用於普通人?

我在網上發現這個example由斯蒂芬莫利,其中包括一個代碼,看起來像你會寫之前縮小它的東西。我採用了該代碼,並通過Google自己的Closure Compiler進行高級優化。正如所料,結果代碼與Google Analytics使用的實際腳本完全不同。

(function(){window.b="ga";"ga"in window||(window.a=function(){window.a.q.push(arguments)},window.a.q=[]);window.a.c=(new Date).getTime();var c=document.createElement("script");c.src="//www.google-analytics.com/analytics.js";c.async=!0;var d=document.getElementsByTagName("script")[0];d.parentNode.insertBefore(c,d)})(); 

這次,即使沒有兩個額外的命令,代碼也不會更乾和更大。所以爲了澄清,我很好奇谷歌工程師是如何到達上述結果的(我認爲他們的代碼實際上看起來不像斯蒂芬的例子中的那個),並且即使你不是一個部分,也可以複製這個過程的谷歌?先謝謝你!

+3

我不知道,但這是一個很小的腳本,它可以很容易地使用所需的函數參數名稱手工完成。 –

+2

...有趣的是,'ga'腳本使用了括號表示法:'i ['GoogleAnalyticsObject']',其中minifiers(包括Closure Compiler)通常會轉換爲點語法,所以這讓我覺得它可能是手工完成的,括號用於防止它通過CC運行,所以它不會將它變成'window.b =「ga」'。 –

+0

@squint謝謝!是的,它似乎是手工完成的,只是想知道我是不是缺少什麼東西 – lmenus

回答

2

我有一種感覺,「isogram」這個詞有點讓Google員工縮小了這段代碼的狡猾暗示。

由於等值線圖是a word with no repeating characters,它代表了縮小參數和其他必須彼此唯一的變量名稱所需的精確邏輯。

很可能,這個術語被烘焙到縮小器中,所以第一個縮小變量的集合將表明他們知道一點關於獨特字母順序的邏輯。

由於isogram這個詞本身就是一個等值線圖,因此創建縮小邏輯的人可以將其設置爲檢查參數或參數列表,以查看7個參數/參數的情況,並且在這種情況下,單詞「isogram」中的相應字母。這會增加一些開銷,但這種情況很少見,Google確實有很多服務器和網絡工程師來優化腳本。

3

這實際上是相當簡單和寫這樣的腳本一個有趣的任務。

這是一個長期的例子,如何把一個普通函數弄成這個樣子:

我與假想腳本啓動。我已經包括它加載異步JavaScript文件中的scriptLoader:在DOM loadScript("/url.js")它會插入一個新的腳本(第一個腳本標記之前)和瀏覽器會下載腳本:

window.loadScript = function(src){ 
    const scriptTag = document.createElement('script'); 
    scriptTag.async = true; 
    scriptTag.src = src; 

    const anyOtherScriptTag = document.getElementsByTagName('script')[0]; 
    anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag); 
} 

當這樣調用。

到目前爲止這麼好。假設我想在加載之前傳遞這個腳本參數。在將被加載的腳本中,我訪問一個唯一的全局對象。我們稱之爲window.myScriptArgs。所以理想情況下,一旦腳本已經加載,它將讀取window.myScriptArgs並相應地執行。現在

我可以做window.myScriptArgs = []和收工,但是因爲我的假設的例子只會加載腳本文件,我的邏輯添加到loadScript功能以及。

window.loadScript = function(src){ 
    window.myScriptArgs = window.myScriptArgs || []; 
    const scriptTag = document.createElement('script'); 
    scriptTag.async = true; 
    scriptTag.src = src; 

    const anyOtherScriptTag = document.getElementsByTagName('script')[0]; 
    anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag); 
} 
loadScript("/my-script.js"); 

好了,所以我檢查是否myScriptArgs已經存在,如果沒有我將其設置爲空數組。 現在我也知道my-script.js公開全局myScript()方法。所以我爲它寫了一個存根。這存根將把每說法得到它進入myScriptArgs數組:

window.myScript =() => { 
    window.myScriptArgs = window.myScriptArgs || []; 
    window.myScriptArgs.push(arguments); 
} 

現在我可以打電話loadScript並立即撥打myScript的()用給定的參數。無需擔心加載問題或不需要。一旦「my-script.js」被加載,它讀取window.myScriptArgs,並作爲例外。代碼如下所示:

window.myScript =() => { 
    window.myScriptArgs = window.myScriptArgs || []; 
    window.myScriptArgs.push(arguments); 
} 

window.loadScript = function(src){ 
    window.myScriptArgs = window.myScriptArgs || []; 
    const scriptTag = document.createElement('script'); 
    scriptTag.async = true; 
    scriptTag.src = src; 

    const anyOtherScriptTag = document.getElementsByTagName('script')[0]; 
    anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag); 
} 
loadScript("/my-script.js"); 
myScript('command', 'args', 'args1'); 
myScript('command2', 'args3', 'args4'); 

好的,按預期工作。讓我們優化它。首先,我的loadScriptmyScript存根相結合,被稱爲initMyScript()一個單一的功能:

window.initMyScript = function(src){ 
    window.myScriptArgs = window.myScriptArgs || []; 
    window.myScript = window.myScript || function(){ 
     window.myScriptArgs.push(arguments); 
    } 

    const scriptTag = document.createElement('script'); 
    scriptTag.async = true; 
    scriptTag.src = src; 

    const anyOtherScriptTag = document.getElementsByTagName('script')[0]; 
    anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag); 
} 
initMyScript("/my-script.js"); 
myScript('command', 'args', 'args1'); 
myScript('command2', 'args3', 'args4'); 

這沒有什麼太花哨大氣壓。現在,我將通過將window作爲參數傳遞給initMyScript來擺脫多個window.調用。我也會用document來做到這一點。

腳本是這樣的:

window.initMyScript = function(p, a, src){ 
    p.myScriptArgs = p.myScriptArgs || []; 
    p.myScript = p.myScript || function(){ 
     p.myScriptArgs.push(arguments); 
    } 

    const scriptTag = a.createElement('script'); 
    scriptTag.async = true; 
    scriptTag.src = src; 

    const anyOtherScriptTag = a.getElementsByTagName('script')[0]; 
    anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag); 
} 
initMyScript(window, document, "/my-script.js"); 

現在讓我們來看看,我重複自己節省一些更多的比特。我使用的字符串script兩次,同爲myScript

window.initMyScript = function(p, a, s, c, src){ 
    p.myScriptArgs = p.myScriptArgs || []; 
    p[c] = p[c] || function(){ 
     p.myScriptArgs.push(arguments); 
    } 

    const scriptTag = a.createElement(s); 
    scriptTag.async = true; 
    scriptTag.src = src; 

    const anyOtherScriptTag = a.getElementsByTagName(s)[0]; 
    anyOtherScriptTag.parentNode.insertBefore(scriptTag, anyOtherScriptTag); 
} 
initMyScript(window, document, 'script', 'myScript', "/my-script.js"); 

在我的旅途下一步是使變量短。而且我也把這個功能變成一個自動執行功能,節省了window.initMyScript定義:

(function(p, a, s, c, src){ 
    p.myScriptArgs = p.myScriptArgs || []; 
    p[c] = p[c] || function(){ 
     p.myScriptArgs.push(arguments); 
    } 

    const q = a.createElement(s); 
    q.async = true; 
    q.src = src; 

    const d = a.getElementsByTagName(s)[0]; 
    d.parentNode.insertBefore(q, d); 
})(window, document, 'script', 'myScript', "/my-script.js"); 

和我最後的神祕:我編輯的功能參數來迷惑人,也更再縮小代碼。你可以使用逗號實際鏈接JavaScript中的函數;)。

(function(p, a, s, c, A, l, i){ 
    p["myScriptArgs"]=p["myScriptArgs"]||[],p[c] = p[c]||function(){ 
     p["myScriptArgs"].push(arguments)}, 
    l = a.createElement(s);l.async = true;l[A] = A; 
    i = a.getElementsByTagName(s)[0]; 
    i.parentNode.insertBefore(l, i); 
})(window, document, 'script', 'myScript', "/my-script.js"); 
myScript("arg1", "arg2"); 
myScript("arg2", "arg3"); 

請注意,我想補充兩個額外的參數的功能,那是因爲我需要保存由createElement返回的元素,並且不希望使用var聲明)。

你可以更進一步,但你明白了。對於小功能,您可以自己動手,沒有問題。

此外,您可以像使用UglifyJS一個minifier,然後自己重​​新命名後的變量,如果你真的成整體等值線圖的事情......

:我沒有測試此代碼的任何。這裏是龍。 想象中的代碼是我糟糕的嘗試去混淆Google例子。谷歌分析代碼片段幾乎與我的自定義代碼片段相同。遺傳算法優化了一些(例如,變成1),但你明白了。

瞭解更多關於我的例子中使用過的東西: Immediately Invoked Function Expression Property accessors (especially Bracket notation)

和JavaScript具體的東西像傳遞三個參數傳遞給這需要5功能。

+0

我不認爲這個答案與這個問題有關,這個問題實際上是關於如何實現這樣一個很好的最小化,這個最小化將以真實單詞「i,s,o,g,r,a,m」爲參數名稱而非如何以異步方式加載JS。你對這個問題的回答似乎是「只用手做」,它沒有提供額外的信息。 – SergGr

+0

除了調整uglifyJS或任何其他縮小器,除了手動完成之外,您無法獲得所需的輸出。 –

4

谷歌是好的,因爲他們給我們介紹一下很多事情的完整文檔上https://developers.google.com

這麼多的響應上可以找到:

這裏是unminified的analytics.js

(function(i, s, o, g, r, a, m){ 
    i['GoogleAnalyticsObject'] = r; // Acts as a pointer to support renaming. 

    // Creates an initial ga() function. 
    // The queued commands will be executed once analytics.js loads. 
    i[r] = i[r] || function() { 
    (i[r].q = i[r].q || []).push(arguments) 
    }, 

    // Sets the time (as an integer) this tag was executed. 
    // Used for timing hits. 
    i[r].l = 1 * new Date(); 

    // Insert the script tag asynchronously. 
    // Inserts above current tag to prevent blocking in addition to using the 
    // async attribute. 
    a = s.createElement(o), 
    m = s.getElementsByTagName(o)[0]; 
    a.async = 1; 
    a.src = g; 
    m.parentNode.insertBefore(a, m) 
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); 

// Creates a default tracker with automatic cookie domain configuration. 
ga('create', 'UA-XXXXX-Y', 'auto'); 

// Sends a pageview hit from the tracker just created. 
ga('send', 'pageview'); 

這裏是他們提供的縮小的版本(版本漂亮):

(function (i, s, o, g, r, a, m) { 
    i['GoogleAnalyticsObject'] = r; 
    i[r] = i[r] || function() { 
      (i[r].q = i[r].q || []).push(arguments) 
     }, i[r].l = 1 * new Date(); 
    a = s.createElement(o), 
     m = s.getElementsByTagName(o)[0]; 
    a.async = 1; 
    a.src = g; 
    m.parentNode.insertBefore(a, m) 
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); 

ga('create', 'UA-XXXXX-Y', 'auto'); 
ga('send', 'pageview'); 

而且here縮小的版本與封編譯工具

(function (a, e, f, g, b, c, d) { 
    a.GoogleAnalyticsObject = b; 
    a[b] = a[b] || function() {(a[b].q = a[b].q || []).push(arguments)}; 
    a[b].l = 1 * new Date; 
    c = e.createElement(f); 
    d = e.getElementsByTagName(f)[0]; 
    c.async = 1; 
    c.src = g; 
    d.parentNode.insertBefore(c, d) 
})(window, document, "script", "//www.google-analytics.com/analytics.js", "ga"); 
ga("create", "UA-XXXXX-Y", "auto"); 
ga("send", "pageview"); 

它看起來像一樣。
您可以在Github repository的項目上找到更多詳細信息。

2

我喜歡認爲有無限多種可能的方式來編寫代碼。 (儘管這可能並非如此)將代碼寫入縮小的位置並可能節省空間的一種方法是使用混淆。 例如,下面的代碼:

function NewObject(prefix) 
{ 
    var count=0; 
    this.SayHello=function(msg) 
    { 
      count++; 
      alert(prefix+msg); 
    } 
    this.GetCount=function() 
    { 
      return count; 
    } 
} 
var obj=new NewObject("Message : "); 
obj.SayHello("You are welcome."); 

可混淆,看起來像這樣:

var _0x3c28=["\x53\x61\x79\x48\x65\x6C\x6C\x6F","\x47\x65\x74\x43\x6F\x75\x6E\x74","\x4D\x65\x73\x73\x61\x67\x65\x20\x3A\x20","\x59\x6F\x75\x20\x61\x72\x65\x20\x77\x65\x6C\x63\x6F\x6D\x65\x2E"];function NewObject(_0x12c4x2){var _0x12c4x3=0;this[_0x3c28[0]]= function(_0x12c4x4){_0x12c4x3++;alert(_0x12c4x2+ _0x12c4x4)};this[_0x3c28[1]]= function(){return _0x12c4x3}}var obj= new NewObject(_0x3c28[2]);obj.SayHello(_0x3c28[3]) 

這是使用上https://javascriptobfuscator.com/Javascript-Obfuscator.aspx無混淆算法來實現。

我相信谷歌有自己的方式來處理他們的代碼當然:)。

1

圖像具有像後續的一段代碼:

(function(){ 
    window.GoogleAnalyticsObject = 'ga'; 
    window.ga = window.ga || function(){ 
    (window.ga.q = window.ga.q || []).push(arguments) 
    }, 
    window.ga.l =1 * new Date(); 
    var a = document.createElement('script'), 
    var m = document.getElementsByTagName('script')[0]; 
    a.async = 1; 
    a.src = '//www.google-analytics.com/analytics.js'; 
    m.parentNode.insertBefore(a, m) 
})(); 

然後,修改代碼以使所有的對象,你需要爲參數:

(function(i, s, o, g, r, a, m){ 
    i['GoogleAnalyticsObject'] = r; 
    i[r] = i[r] || function(){ 
    (i[r].q = i[r].q || []).push(arguments) 
    }, 
    i[r].l =1 * new Date(); 
    a = s.createElement(o), 
    m = s.getElementsByTagName(o)[0]; 
    a.async = 1; 
    a.src = g; 
    m.parentNode.insertBefore(a, m) 
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); 

刪除所有的空格和最後你得到:

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 

我希望我很清楚,再見。

更新:你問他們爲什麼選擇單詞「isogram」?它是「已知」等值線詞之一,如果您需要更多參數,請參閱Wikipedia

-1

你可以使用npm和一個任務跑步者像吞嚥。 Gulp有一個名爲uglify的插件,它可以消除多餘的空間並獲取參數和變量,並將它們減少爲一個字母,以進一步減少代碼中的字符總數。

相關問題