Статьи

Автор: Мациевский Николай aka sunnybear
Опубликована: 16 сентября 2008

Разгоняем счетчики: от мифов к реальности

Не только начинающие, но и продвинутые оптимизаторы встают в тупик, когда речь заходит о счетчиках посещаемости — ведь обычно это JS-код, который нужно вставить на страницу (и хорошо еще, если не требуют максимально близко к открывающему тегу body) и который нельзя никак менять. Иначе статистика просто не будет работать.

На самом деле, все не так плохо. Скорее, все очень хорошо, но мало кто об этом знает :)

Заглянем внутрь

Что из себя представляет код JS-счетчика? Обычно (в 99% случаев) он «вытаскивает» из клиентского окружения набор параметров (URL текущей страницы, URL страницы, с который перешли на текущую, браузер, ОС и т.д.), которые передаются на сервер статистики. Все навороты счетчиков связаны с обеспечением максимальной точности передаваемой информации (кроссбраузерность, фактически). Наиболее мощные (Omniture, Google Analytics) используют еще и собственные переменные и события, чтобы усилить маркетинговую составляющую.

Но сейчас речь не об этом. Как собранные на клиенте данные попадают на сервер статистики? Все очень просто: в документе создается уникальный элемент, в URL которого «зашиваются» все необходимые значения (обычно в качестве GET-параметров). URL этот ведет, как можно догадаться, на сервер статистики, где данные кладутся в базу и каким-то образом показываются в администраторском интерфейсе.

Как же создается этот самый «уникальный» элемент? Так сложилось, что наиболее простым транспортным средством для данных стала картинка. Обычный однопиксельный GIF-файл (сейчас, в эпоху CSS-верстки, это, пожалуй, единственное его применение) отдается сервером в ответ на URL с параметрами от клиента.

Разбираем по косточкам

Итак, что нам предстоит? Нам нужно гарантировать загрузку внешнего JS-файла «ненавязчивым» образом, при этом обеспечить запрос на сервер статистики (создание картинки со специальными параметрами). В моем случае (речь пойдет про Google Analytics) все будет очень тривиально, ибо картинка создается через new Image(1,1). Однако, большинство счетчиков (Рунета и не только) оперируют document.write, если такая конструкция отработает после создания основного документа, то браузер просто создаст новый, в который запишет требуемый результат. Для пользователя это выльется в белую страницу в браузере.

Основная сложность в переносе скриптов статистики в загрузку по комбинированному window.onload заключается как раз в изменении вызова картинки, обеспечивающей сбор статистики, на DOM-методы (это может быть не только new Image, но и appendChild). В качестве примера рассмотрим преобразование скрипта статистики для LiveInternet:

document.write("<img src='http://counter.yadro.ru/hit;tutu_elec?r"+
escape(document.referrer)
+((typeof(screen)=="undefined")?"":";s"+screen.width+"*"+screen.height+"*"
+(screen.colorDepth?screen.colorDepth:screen.pixelDepth))
+";u"+escape(document.URL)+";"+Math.random()+"' width=1 height=1 alt=''>")

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

new Image(1,1).src='http://counter.yadro.ru/hit;tutu_elec?r"
+escape(document.referrer)+((typeof(screen)=="undefined")?"":";s"
+screen.width+"*"+screen.height+"*"
+(screen.colorDepth?screen.colorDepth:screen.pixelDepth))
+";u"+escape(document.URL)+";"+Math.random()

Таким образом (все приведенные участки кода — это одна строка, просто разбита для удобства чтения), мы просто заменили вызов document.write на new Image(). Это поможет в большинстве случаев. Если у вас ситуация не сложнее уже описанной, то следующие абзацы можно пропустить.

А если сложнее?

Не все счетчики одинаково просты :) Например, для сбора статистики с помощью того же Google Analytics нам нужно загрузить целую библиотеку — файл urchin.js. На наше счастье конкретно в этом скрипте данные уже собираются с помощью создания динамической картинки.

На самом деле, все, что нам требуется в том случае, если во внешней библиотеке находится мешающим нам вызов document.write, это заменить его соответствующим образом. Обычно для этого необходимо изменить сам JS-файл. Я не буду далеко ходить за примером и рассмотрю преобразования на примере Omniture, довольно популярной на западе библиотеки для сбора статистики.

Сначала нам нужно найти соответствующий участок кода внутри JS-файла. В нашем случае это будет возвращаемая строка, которая затем вписывается в документ:

var s_code=s.t();if(s_code)document.write(s_code)

В коде Omniture достаточно найти соответствующий return

return '<im'+'g sr'+'c="
+"\"'+rs+'\" width=1 height=1 border=0 alt=\"\">'

И заменить его на следующий код (заметим, что для src картинки берется переменная rs)

return 'new Image(1,1).src=\"'+rs+'\"'

Затем мы уже можем заменить вызов и в самом HTML-файле на

var s_code=s.t();if(s_code)eval(s_code)

Для того, чтобы все окончательно заработало, необходимо заменить в файле s_code.js и остальные вызовы document.write (всего я насчитал их два там). Выглядит это примерно так:

var c=s.t();if(c)s.d.write(c);
...
s.d.write('<im'+'g name=\"'+imn+"
+"'\" height=1 width=1 border=0 alt=\"\">');

меняем на

var c=s.t();if(c)eval(c);
...
new Image(1,1).name=imn;

Примечание: опытные оптимизаторы заметили, что альтернативой document.write в нашем случае стал eval, что, по большому счету, не очень хорошо (eval is evil). Однако, я не хочу перебирать данный конкретный скрипт по косточкам, чтобы избавиться от такого костыля. Мне проще быть уверенным, что вся остальная логика останется нетронутой после моих вмешательств, ибо они касались только отправки собранных данных на сервер.

Делаем статистику динамической

В предыдущем разделе мы узнали, как подготовить внешний JS-файл к динамической загрузке. Осталось понять, как теперь это использовать.

Основное преимущество (или недостаток?) Omniture заключается в том, что JS-файл (обычно s_code.js) располагается на нашем сервере. Поэтому ничего не мешает нам его там и заменить . После этого обеспечить динамическую загрузку и вызов счетчика уже не составит труда.

В той ситуации, когда скрипт совсем внешний (Google Analytics) у нас, по большому счету, только 2 выхода:

  • Перенести сам скрипт на наш сервер, добавить в него необходимые инициализационные переменные и вызов (помимо самого объявления) функции статистики (для Google Analytics это urchinTracker()). В качестве плюсов можно отметить то, что, в общем случае, скрипт будет грузиться с вашего сервера побыстрее, чем будет устанавливаться новое соединение с google-analytics.com и проверяться, что файл не изменился. В качестве минусов — необходимость отслеживать (возможные) изменения скрипта и необходимость отдавать JS-файл с собственного сервера со всеми вытекающими последствиями.
  • Проверять по таймауту, загрузилась ли библиотека. Пишется очень простой код, который через каждый 10мс проверяет, доступна ли необходимая из библиотеки функции. Если да, то она вызывается. В противном случае проверка запускается снова через 10мс. Плюсы: можно использовать тот же самый скрипт, что и раньше. Минусы: дополнительная (небольшая) нагрузка на клиент при загрузке.

Получаем, что в первом случае у нас загрузка сильно ускоряется (особенно для постоянных посетителей), во втором случае — получается дешево и сердито. Зато более надежно (в смысле отсутствия дополнительного вмешательства кривых рук :).

Практическое заключение

Надеюсь, все вышеприведенное поможет оптимизаторам еще ускорить загрузку страниц даже при наличии на них внешних счетчиков. В качестве примера могу привести собственный вариант для Google Analytics — g.js, который сейчас загружается на webo.in.

P.S. Поводом для написания данной заметки послужила статья «Клиентская оптимизация и этапы разработки».

P.P.S. Не стоит воспринимать все вышеописанное, как прямое указание к действию. Это не более, чем очередной хинт для продвинутых оптимизаторов :)

Читать дальше

Все комментарии (habrahabr.ru)