12

Большинство из вас, вероятно, знакомы с этим небольшим кодом отслеживания, предлагаемым Google Analytics.Как минимизировать JavaScript, как Google Analytics?

<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> 

Интересная часть состоит в том, что эта небольшая выдержка содержит аргументы, которые образуют слово изограмма. Этот скрипт также использует аргументы для объявления переменных, чтобы сбрить некоторые биты из окончательного размера файла. Возможно, вы не использовали бы этот шаблон при написании кода (?), Поэтому мой вопрос: как Google минимизирует свой код и эти методы доступны для простых смертных?

Я нашел онлайн это example от Стивена Морли, который включает в себя код, который выглядит как что-то, что вы пишете, прежде чем его использовать. Я взял этот код и запустил его через собственный Closure Compiler от Google по продвинутой оптимизации. Как и ожидалось, полученный код не похож на фактический скрипт, используемый 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)})(); 

На этот раз код меньше DRY и больше, даже без двух дополнительных команд.

Так, чтобы уточнить, мне любопытно, как инженеры Google пришли к вышеуказанному результату (я не думаю, что их код на самом деле выглядит так, как на примере Стивена), и может ли этот процесс быть реплицирован, даже если вы не являетесь частью Google? Заранее спасибо!

+3

Я не знаю, но это такой крошечный скрипт, его можно легко выполнить вручную, используя желаемые имена параметров функции. –

+2

... Интересно, что скрипт 'ga' использует скобку с строковым литералом:' i ['GoogleAnalyticsObject'] ', какие миниторы (включая Closure Compiler) обычно преобразуют в синтаксис точек, поэтому это заставляет меня думать, что это вероятно, выполняется вручную, при этом скобки используются только в том случае, если он выполняется через CC, поэтому он не превращает его в 'window.b =" ga "'. –

+0

@squint Спасибо! Да, похоже, что это было сделано вручную, было просто интересно, не пропал ли я что-то. – lmenus

ответ

2

У меня такое чувство, что слово «изограмма» - это хитрая подсказка от сотрудника Google, который минимизировал этот код.

Поскольку изограмма a word with no repeating characters, она представляет собой точную логику, необходимую для минимизации параметров и других имен переменных, которые должны быть уникальными друг от друга.

По всей вероятности, этот термин был запечен в minifier, так что первый набор миниатюрных переменных указывает, что они немного знают о логике уникальной последовательности букв.

Поскольку слово isogram само по себе является изограммой, человек, создавший логику минимизации, может установить его для проверки списка параметров или аргументов для случая, в котором есть 7 аргументов/параметров, и в этом случае просто заменить каждый на соответствующую букву в слове «изограмма». Это добавит некоторые накладные расходы, но такие случаи встречаются редко, и у Google есть много серверов и сетевых инженеров для оптимизации своих сценариев.

3

На самом деле это довольно простая и забавная задача написать такие скрипты.

Вот большой пример того, как превратить обычную функцию в нечто вроде этого:

Я хотел бы начать с воображаемым сценарием. Я включил scriptLoader, который загружает файл яваскрипт асинхронно:

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); 
} 

Когда названные так: loadScript("/url.js") будет вставить новый тег сценария (до первого тега сценария) в DOM и браузер загрузит скрипт ,

Пока все хорошо. Предположим, я хочу передать аргументы этого скрипта до его загрузки. Внутри скрипта, который будет загружен, я получаю доступ к уникальному глобальному объекту. Назовем это 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'); 

Хорошо, работает нормально.Давайте его оптимизировать. Сначала я объединить loadScript и myScript окурок к одной функции, называемой 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., передав window в качестве аргумента initMyScript. Я также сделаю это с 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 заявление;).

Вы можете принять это еще дальше, но вы понимаете. Для небольших функций вы можете сделать это самостоятельно без проблем.

Кроме того, вы можете использовать Minifier как UglifyJS, а затем переименовать переменные себя после этого, если вы действительно в этом всю изограмме вещи ...

Примечание: Я не испытывал какие-либо из этого кода. Здесь будут драконы. Мнимый код - моя неудачная попытка де-обфускации примера Google. Фрагмент google-analytics работает почти так же, как и мой пользовательский фрагмент. GA оптимизирует немного больше (например, превращая true в 1), но вы получите точку.

Узнайте больше о вещах, используемых в моем примере: Immediately Invoked Function Expression Property accessors (especially Bracket notation)

И JavaScript конкретных вещей, как проходит три аргумента в функцию, которая принимает 5.

+0

Я не думаю, что этот ответ имеет отношение к вопросу о том, как эффективно достичь такой хорошей минимизации, которая будет содержать реальные слова 'i, s, o, g, r, a, m' имен аргументов, а не как загрузить JS асинхронным способом. И ваш ответ на этот вопрос кажется «просто сделайте это вручную», который не дает дополнительной информации. – SergGr

+0

Ну, кроме настройки uglifyJS или любого другого minifier, вы не можете получить желаемый результат, за исключением того, что делаете это вручную. –

4

Google хорошо, потому что они дают нам полную документацию о многой вещи на 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 Минимизированную версию с помощью инструментов Closure Compiler

(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.

Я уверен, что у Google есть свои способы управления своим кодом курса :).

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'); 

Надеюсь, я был прост, пока.

Обновлено: Вы спрашиваете, почему они выбирают слово «изограмма»? Это один из «известных» слов изограммы, см. Wikipedia, если вам нужно больше параметров.

-1

Вы можете использовать npm и бегун задачи, как глоток. Gulp имеет плагин под названием uglify, который устраняет лишние пробелы и принимает параметры и переменные и сводит их к одной букве, чтобы еще больше уменьшить общее количество символов в коде.