Итак, мы оставили нового пользователя наедине с единственным JavaScript-файлом, не включающим ничего лишнего. Стал ли при этом пользователь счастливее? Ничуть. Наоборот, в среднем пользователь стал более несчастным, чем раньше, а причина этому — увеличившееся время загрузки страницы.
Немного из теории HTTP-запросов
Время загрузки ресурса через HTTP-соединение складывается из следующих основных элементов:
время отсылки запроса на сервер T1 — для большинства запросов величина практически постоянная;
время формирования ответа сервера — для статических ресурсов, которые мы сейчас и рассматриваем, пренебрежимо мало;
время получения ответа сервера T2, которое, в свою очередь, состоит из постоянной для сервера сетевой задержки L и времени получения ответа R, прямо пропорционального размеру ресурса.
Итак, время загрузки страницы будет состоять из времени загрузки HTML-кода и всех внешних ресурсов: изображений, CSS- и JavaScript-файлов. Основная проблема в том, что CSS и JavaSscript-файлы загружаются последовательно (разработчики браузеров уже работают над решением этой проблемы в последних версиях, однако пока еще 99% пользователей страдают от последовательной загрузки). В этом случае общение с сервером выглядит так:
— запросили страницу
— получили HTML
— запросили ресурс A: T1
— получили ресурс A: L + R(A)
— запросили ресурс B: T1
— получили ресурс B: L + R(B)
— запросили ресурс C: T1
— получили ресурс C: L + R(C)
Общие временные затраты при этом составят 3(T1+L) + R(A+B+C).
Объединяя файлы, мы уменьшаем количество запросов на сервер:
— запросили страницу
— получили HTML
— запросили ресурс A+B+C: T1
— получили ресурс A+B+C: L + R(A + B + C)
Очевидна экономия в 2(T1 + L).
Для 20 ресурсов эта экономия составит уже 19(T1 + L). Если взять достаточно типичные сейчас для домашнего/офисного Интернета значения скорости в 256 Кбит/с и пинга ~20-30 мс, получим экономию в 950 мс — одну секунду загрузки страницы. У людей же, пользующихся мобильным или спутниковым интернетом с пингом более 300 мс, разница времен загрузки страниц составит 6-7 секунд.
На первый взгляд, теория говорит, что загрузка страниц должна стать быстрее. В чем же она разошлась с практикой?
Пусть у нашего сайта есть три страницы — P1, P2 и P3, поочередно запрашиваемые новым пользователем. P1 использует ресурсы A, B и C, P2 — A, С и D, а P3 — A, С, E и F. Если ресурсы не объединять, получаем следующее:
P1 — тратим время на загрузку A, B и C
P2 — тратим время на загрузку только D
P3 — тратим время на загрузку E и F
Если мы слили воедино абсолютно все JavaScript-модули сайта, получаем:
P1 — тратим время на загрузку (A+B+C+D+E+F)
P2 — внешние ресурсы не требуются
P3 — внешние ресурсы не требуются
Результатом становится увеличение времени загрузки самой первой страницы, на которую попадает пользователь. При типовых значениях скорости/пинга мы начинаем проигрывать уже при дополнительном объеме загрузки в 23 Кб.
Если мы объединили только модули, необходимые для текущей страницы, получаем следующее:
P1 — тратим время на загрузку (A+B+C)
P2 — тратим время на загрузку (A+C+D)
P3 — тратим время на загрузку (A+С+E+F)
Каждая отдельно взятая страница при пустом кэше будет загружаться быстрее, но все они вместе — медленнее, чем в исходном случае. Получаем, что слепое использование модного сейчас объединения ресурсов часто только ухудшает жизнь пользователя.
Конечно же, выход из сложившегося положения есть. В большинстве случаев для получения реального выигрыша достаточно выделить «ядро» — набор модулей, используемых на всех (или, по крайней мере, на часто загружаемых) страницах сайта. Например, в нашем примере достаточно выделить в ядро ресурсы A и B, чтобы получить преимущество:
P1 — тратим время на загрузку (A + B) и C
P2 — тратим время на загрузку D
P3 — тратим время на загрузку (E + F)
Вдумчивый читатель сейчас возмутится и спросит: «А что, если ядра нет? Или ядро получается слишком маленьким?». Ответ: это легко решается вручную выделением 2-3 независимых групп со своими собственными ядрами. При желании задачу разбиения можно формализовать и получить точное машинное решение — но это обычно не нужно; руководствуясь простейшим правилом — чем больше ядро, тем лучше, — можно добиться вполне приличного результата.