Опубликован: 16.02.2009 | Доступ: свободный | Студентов: 1426 / 138 | Оценка: 4.26 / 4.17 | Длительность: 16:10:00
ISBN: 978-5-9963-0024-2
Лекция 6:

CSS-оптимизация

< Лекция 5 || Лекция 6: 12345 || Лекция 7 >

"Экономия на спичках"?

В результате тестов удалось показать, что валидный XHTML не медленнее (а даже местами быстрее), чем HTML. И оптимизация реально играет роль (возможно ускорение загрузки HTML главной страницы Яндекса на 10–12%). Если говорить о конкретных примерах, то на 100 Кб/с канале с включенным сжатием в FF3 оптимизированный вариант загрузится на 9 мс быстрее. Для более быстрого канала или более медленного компьютера отличие будет еще разительнее.

Естественно, это все "копейки" для обычных пользователей (+/-50 мс — это совершенно не критично). Однако если речь идет про "экономию на спичках", когда нам важен каждый запрос к серверу и каждая миллисекунда, то тут уже стоит задуматься — что же все-таки использовать.

И что важнее всего, если правильно расставить акценты, то загрузку XHTML можно сделать и быстрее, чем HTML. Различие в размере файлов оказалось в итоге минимальным (26153 против 26275 в несжатом варианте, и 8862 против 8845 в сжатом, т. е. меньше 0,5%). При этом в IE7 наблюдается ускорение отображения страницы на 7 мс (от 60–80 мс при загрузке страницы). Это в среднем дает 10% выигрыша в скорости. FF3 ведет себя похожим образом (но выигрыш в скорости 20% (25 мс от 127 мс)). Все остальные браузеры показали отличие в загрузке на 2-3 мс, что укладывается в погрешность; Opera была медленнее, что подтверждается предыдущими тестами.

В целом в свете тотального распространения мобильных браузеров с их маломощными процессорами такой вид оптимизации выглядит весьма перспективно.

Ни в коем случае не reflow!

В CSS-движке браузеров существует несколько операций, затрагивающих изменение картинки на экране браузера. В предыдущих тестах было рассмотрено начальное создание документа и способы ускорения это процесса. Однако все вышеприведенные советы в равной степени относятся и к изменению уже отрисованной картинки при каких-либо операциях с документом.

HTML-элемент в документе может быть скрыт с помощью JavaScript или CSS-свойства display. Дублировать с помощью JavaScript логику, заложенную в CSS-движке, достаточно сложно и не всегда нужно. Проще запросить offsetHeight объекта (если оно равно 0, значит элемент скрыт). Проще-то оно конечно проще, вот только какой ценой?

Для проверки видимости элемента принято проверять значение стиля display или наличие класса hide. Когда мы пишем функцию скрытия/отображения сами, то знаем, какое значение стиля display у объекта по умолчанию или какой класс какому состоянию соответствует. Однако универсальная (библиотечная) функция знать об этом не может.

offsetHeight и style.display

Проведем тестирование скорости вычисления значений offsetHeight и style.display.

Для удобства профайлинга вынесем доступ к этим значениям в отдельные функции:

function fnOffset(el)
{
 return !!el.offsetHeight;
}

function fnStyle(el)
{
 return el.style.display=='none';
}

где el — тестовый контейнер.

Проведем тест на тысяче итераций, на каждой итерации будем добавлять в тестовый контейнер элемент <span>. Проверим время, затрачиваемое на добавление тысячи элементов, без вызова тестовых функций тест clean. Проведем тестирование во всех браузерах, замеряя время следующим способом:

var time_start=new Date().getTime();
/* ... тест ... */
var time_stop=new Date().getTime();
var time_taken=time_stop-time_start;

где time_taken — это время, затраченное на тест, в миллисекундах.

Таблица 6.3. Результаты выполнения тестов по определению видимости элементов. Времена приведены в миллисекундах. Взято среднее значение серии из 5 тестов
IE sp62 IE8b Firefox 2.0.0.12 Opera 9.22 Safari 3.04b
Clean 128 153 15 15 16
offsetHeight 23500 10624 4453 4453 5140
style.display 171 209 56 56 34
height vs. Style 140 раз 50 раз 80 раз 80 раз 150 раз

Судя по результатам тестов, доступ к offsetHeight медленнее в 50-150 раз.

Получается, что по отдельности и offsetHeight, и добавление элементов работают быстро, а вместе — очень медленно. Как же так?

Немного теории

Reflow — это процесс рекурсивного обхода ветви дерева DOM, вычисляющий геометрию элементов и их положение относительно родителя. Начало обхода — изменившийся элемент, но возможно и распространение в обратном порядке. Существуют следующие типы reflow:

  • начальный — первичное отображение дерева;
  • инкрементный — возникает при изменениях в DOM;
  • изменение размеров;
  • изменение стилей;
  • "грязный" — объединение нескольких инкрементных reflow, имеющих общего родителя.

Reflow делятся на неотложные (изменение размеров окна или изменение шрифта документа) и асинхронные, которые могут быть отложены и объединены впоследствии.

При манипулировании DOM происходят инкрементные reflow, которые браузер откладывает до конца выполнения скрипта. Однако исходя из определения reflow, "измерение" элемента вынудит браузер выполнить отложенные reflow. Т.к. возможно распространение снизу вверх, то выполняются все reflow, даже если измеряемый элемент принадлежит к неизменившейся ветви.

Операции reflow очень ресурсоемки и являются одной из причин замедления работы веб-приложений.

Если судить по тесту clean, все браузеры хорошо справляются с кэшированием многочисленных reflow. Однако запрашивая offsetHeight, мы "измеряем" элемент, что вынуждает браузер выполнить отложенные reflow. Таким образом, браузер делает тысячу reflow в одном случае и только один — в другом.

Замечание: в Opera reflow выполняется еще и по таймеру, что, однако, не мешает ей пройти тест быстрее остальных браузеров. Благодаря этому в Opera виден ход тестов — появляются добавляемые звездочки. Такое поведение оправдано, т. к. вызывает у пользователя ощущение большей скорости браузера.

Использование Computed Style

Что же показало тестирование? По меньшей мере, некорректно сравнивать универсальный ( offsetHeight ) и частный ( style.display ) случаи. Тестирование показало, что за универсальность надо платить. Если же все-таки хочется универсальности, то можно предложить другой подход: определение Computed Style — конечного стиля элемента (после всех CSS-преобразований).

getStyle = function()
{
 var view = document.defaultView;

 if(view && view.getComputedStyle)
 	return function getStyle(el,property)
	{
		return view.getComputedStyle(el,null)[property] ||
			el.style[property];
	};

 return function getStyle(el,property)
 {
	return el.currentStyle && el.currentStyle[property] ||
		el.style[property];
 };
}();

Проведем тестирование этого способа и сведем все результаты в таблицу.

Таблица 6.4. Результаты выполнения функции getStyle (времена приведены в миллисекундах)
IE sp62 Firefox 2.0.0.12 Opera 9.22 Safari 3.04b
offsetHeight 23500 4453 4453 5140
style.display 171 56 56 34
getStyle 5219 5318
< Лекция 5 || Лекция 6: 12345 || Лекция 7 >