Ajax в действии
.pdf}
ClickBox.prototype.render=function(){
this.body=null;
if (this.body==null){ this.body=document.createElement("div"); this.body.id=this.id;
}
this.body.className='boxl';
this.body.style.left=this.x+"px";
this.body.style.top=this.y+"px";
this.body.onclick=function(){ var clickbox=this.backingObj; clickbox.incrementState();
}
}
ClickBox.prototype.incrementState^function(){ if (this.state==O){
this.body.className='box2' ; }else if (this.state==l){
this.hide{);
}
this.state++;
}
ClickBox.prototype.hide=function(){ var bod=this.body;
b o d . c l a s s N a m e = ' Ь о х З' ;
}
Компонент ClickBox имеет красный цвет, если отображается впервые. После того как пользователь один раз щелкнет на нем мышью, цвет компонента изменяется на синий. После второго щелчка компонент удаляется с экрана. Данное поведение реализуется путем создания двусторонних ссылок между объектами модели предметной области и элементами DOM, представляющими их на экране.
Впрограмме для каждого ClickBox поддерживается уникальный идентификатор, информация о позиции, сведения о внутреннем состоянии (сколько щелчков было сделано) и тело компонента. Телом компонента является узел DOM типа div. Узел DOM содержит ссылку на объект модели в переменной backingObj.
Впрограмме также определен класс Container, содержащий объекты ClickBox. В данном классе содержится массив компонентов и уникальный идентификатор самого контейнера.
function Container(id){ this.id=id;
this.body=document.getElementByld(id) ; this.boxes=new Array();
}
Container.prototype.add=function(box){
this.boxes[this.boxes.length]=box;
this.body.appendChild(box.body);
}
Container.prototype.clear=function(){
Рис. 8.10. Так выглядит приложение, демонстрирующее использование памяти, после создания первых 100 компонентов. Компоненты реагируют на щелчки мышью
f o r ( v a r i = Q ; i < t h i s . b o x e s . l e n g t h ; i + + ) { t h i s . b o x e s [ i ] . h i d e ( ) ;
}
this.boxes^new Array(); report("clear"); newDOMs=0; reusedDOMs=0;
}
Внешний вид окна приложения показан на рис. 8.10.
На отладочной панели, расположенной справа, отображается информация о внутреннем состоянии системы. На состояние системы влияют события, например, добавление компонентов к контейнеру или их удаление.
Данная программа была написана для того, чтобы в процессе ее работы можно было поэкспериментировать с созданием и удалением элементов DOM и обработкой циклических ссылок. Пользователь может выбирать различные приемы работы с программой, устанавливая или удаляя флажки опций в HTML-форме, расположенной на странице. При активизации ссылок, управляющих добавлением элементов в контейнер или их удалением, реализуются программные решения, определяемые набором установленных флажков. Рассмотрим каждый из этих флажков и соответствующий ему код.
Опция Reuse DOM Nodes
Данная опция определяет, должен ли компонент ClickBox при отображении пытаться найти существующий узел DOM и создавать новый узел, только если поиск закончился неудачей. Другими словами, флажок этой опции осуществляет переключение между режимами создания при любых условиях и создания при отсутствии (см. раздел 8.3.2). Модифицированный код, отвечающий за воспроизведение компонента, выглядит следующим образом:
ClickBox.prototype.render=function(){
this.bodyenull;
340 Часть III Создание профессиональных Ajax-приложений
if (reuseDOM){ this.body=document.getElementById(this. id);
}
if (this.body==null){ this.body=document.createElement("div"); this.body.id=this. id;
newD0Ms++;
}else{
reusedDOMs++;
}
this.body.backingObj =this; this.body.className='boxl'; this.body.style.left=this.x+"px"; this.body.style.top=this.y+"px"; this.body.onclick=function(){
var clickbox=this.backingObj; clickbox.incrementState();
}
}
Опция Unlink On Hide
Когда компонент ClickBox удаляется из контейнера (либо после второго щелчка мышью, либо в результате вызова Container. clear ()), данная опция определяет, должно ли производиться удаление путем сокрытия или удаление путем отсоединения. Об этих принципах удаления см. в разделе 8.3.2.
ClickBox.prototype.hide=function(){ var bod=this.body; bod.className='box3';
if (unlinkOnHide){
bod.parentNode.removeChiId(bod};
}
}
/
Опция Break Cyclic References
Данная опция также имеет отношение к процессу удаления компонента ClickBox. Она определяет, должны ли присваиваться значения null свойствам элемента D0M и объекта модели предметной области, т.е. должны ли удаляться циклические ссылки.
ClickBox.prototype.hide=function(){ var bod=this.body; bod,className='box3' ;
if (unlinkOnHide){
bod.parentNode.removeChild(bod);
}
if {breakCyclics){
bod.backingObj=null;
this.body=null;
I
}
Управляющие элементы формы позволяют пользователю помещать з контейнер новые компоненты ClickBoxes и очищать контейнер. Приложением можно управлять вручную, но для удобства сбора результатов мы написали также тестирующую функцию, которая имитирует некоторые действия пользователя. Приведенная ниже последовательность действий повторяется 240 раз.
1.Включение 100 компонентов в контейнер с помощью функции populate ().
2.Добавление 100 компонентов.
3.Очистка контейнера.
Код функции stressTest() приведен ниже.
function stressTest() {
for (var i=Q;i<240;i++){ populate (100); populate(100); container.clear();
}
alert("done");
{
Заметьте, что в данном случае проверяется добавление компонентов к контейнеру и удаление их, а не поведение элементов ClickBox по щелчку мышью.
Данная проверка умышленно упрощена. Если вам надо лишь выяснить, увеличивается или уменьшается объем потребляемой памяти при изменении алгоритма, мы рекомендуем создавать для ваших приложений такие же простые тесты. Создание тестовых сценариев уже само по себе требует определенного мастерства, в частности, от разработчика требуется знание типичных приемов проектирования.
Выполнение тестового сценария занимает около минуты. В течение этого времени браузер не реагирует на действия пользователя. Если число итераций выбрать слишком большим, браузер может "зависнуть" на длительное время. Если число повторений мало, изменения потребляемой памяти не будут заметны. Мы выбрали 240 итераций и считаем, что для нашей машины это вполне приемлемое значение. Вы же можете изменить его, исходя из конкретных условий.
Запись изменения занимаемой памяти — сравнительно простая задача. Мы выполняем тестирование в операционной системе Windows при работающей программе Task Manager. Объем потребляемой памяти регистрируется непосредственно после загрузки тестовой страницы, а затем снова после вывода окна, сообщающего о том, что тестирование завершено. В системе Unix для тестирования может быть использована утилита top или другой инструмент аналогичного назначения (см. раздел 8.4.1). После каждой проверки мы закрываем браузер, чтобы обеспечить равные условия тестирования.
Таблица 8.4. Результаты тестирования
Идентификатор |
Флажок Reuse |
Флажок Unlink |
Флажок Break |
Занимаемая память |
|
DOM Nodes |
On Hide |
Cyclic |
после окончания |
|
|
|
References |
работы (Internet |
|
|
|
|
Explorer) |
А |
Сброшен |
Сброшен |
Сброшен |
166 Мбайт |
В |
Сброшен |
Сброшен |
Установлен |
84,5 Мбайт |
С |
Сброшен |
Установлен |
Сброшен |
428 Мбайт |
D |
Установлен |
Сброшен |
Сброшен |
14,9 Мбайт |
Е |
Установлен |
Сброшен |
Установлен |
14,6 Мбайт |
F |
Установлен |
Установлен |
Сброшен |
574 Мбайт |
G |
Установлен |
Установлен |
Установлен |
14,2 Мбайт |
Такова методология проверки. В следующем разделе мы обсудим ее результаты.
8.4.3. Как уменьшить объем используемой памяти в 150 раз
Выполняя тестирование и пользуясь данными, предоставляемыми Windows Task Manager, можно сделать вывод о том, что объем используемой памяти зависит от алгоритмов, выбранных посредством флажков опций. Результаты проверки приведены в табл. 8.4.
Тестирование производилось на рабочей станции с тактовой частотой процессора 2,8 ГГц и 1 Гбайт оперативной памяти, работающей под управлением операционной системы Windows 2000 Workstation. В качестве браузера был выбран Internet Explorer 6. Во всех случаях начальное потребление памяти составляло приблизительно 11,5 Мбайт. Информация об использовании памяти отображалась в столбце Mem Usage на вкладке Processes программы Task Manager (см. раздел 8.4.1).
Поскольку мы сопоставляем реальные значения, следует заметить, что само приложение занимает достаточно много памяти. Ajax-программы считаются "тонкими" клиентами, однако при определенных ошибках в коде или их сочетании они могут потреблять невероятно много памяти.
Необходимо также заметить, что выбор конкретных программных решений оказывает огромное влияние на объем занимаемой памяти. Рассмотрим результаты более подробно. При трех сочетаниях флажков опций после отображения и удаления всех компонентов clickBox объем памяти оказывался менее 15 Мбайт. При остальных наборах опций это значение возрастало до 80, 160 и даже до 430 и 580 Мбайт. Учитывая, что сам браузер потребляет 11,5 Мбайт памяти, размер дополнительно использованной памяти лежал в пределах от 3,5 до 570 Мбайт, т.е. мы можем сократить его в 150 раз, правильно выбрав программные решения. Стоит также отметить, что браузер продолжал функционировать при любой утечке памяти.
Глава 8. Производительность приложения 343
Ни один из алгоритмов нельзя признать виновником происходящего. у[ежду ними существует очень сложная взаимозависимость. Сравнивая, например, проверки A, D и F, мы видим, что переключение значения опции Reuse DOM Nodes дает огромную экономию памяти {больше 90%), в то же время переключение Unlink On Hide приводит к тройному увеличению ее похребления. В данном конкретном случае причины происходящего понятны — поскольку связи для узлов DOM разрываются, они не могут быть найдены путем вызова document .getElementById(), следовательно, их невозможно использовать повторно. Аналогично, изменение значения опции Unlink on Hide приводит к увеличению потребления памяти по сравнению с исходной конфигурацией (проверки С и А). Перед тем как объявлять опцию Unlink on Hide виновницей излишнего расхода памяти, сравним проверки Е и G — в правильном контексте она дает некоторый положительный результат.
Интересно отметить, что в этом "состязании" нет победителя: три совершенно различные комбинации значений опций дают лишь незначительное различие в объеме потребляемой памяти. Во всех трех случаях установлен флажок Reuse DOM Nodes, но эта же опция в другом сочетании приводит к максимальному расходу памяти. Однозначного вывода из имеющихся результатов сделать нельзя, но мы можем выделить сочетания алгоритмов, которые дают хорошие результаты, и другие наборы, которые использовать не следует. Если мы проанализируем программные решения и дадим им имена, будет значительно проще применять их при работе над приложением для получения приемлемых результатов. Если мы не будем использовать фиксированный набор программных решений, а будем по наитию создавать каждую подсистему, работающую с DOM, каждый новый фрагмент кода будет давать непредсказуемые результаты: возможно, его выполнение приведет к значительному расходованию памяти, а может быть, он будет работать достаточно экономно.
В ходе подобных проверок разработчик получает информацию, позволяющую решить многие вопросы, связанные с разработкой богатых DHTMLклиентов, выполняющихся в среде браузера в течение длительного времени, и выявить ряд ошибок.
Чтобы реально контролировать вопросы, связанные с использованием памяти, надо помнить о них в процессе разработки. Модифицируя код, полезно подумать о том, как эти изменения скажутся на использовании памяти. Кроме того, после внесения изменений всегда надо проверять, как память используется программой.
Несмотря на то что стандартные программные решения помогают в разработке, проблемы с памятью вновь могут возникнуть при незначительном изменении условий. Так, например, мы знаем, что алгоритм устранения циклических ссылок может давать неожиданные результаты, взаимодействуя с решением, которое предполагает создание узлов лишь в случае их отсутствия. Детально представляя себе суть того или иного программного решения, вы уменьшаете вероятность возникновения нежелательных последствий.
Использование типичных программных решений упрощает задачу создания тестовых сценариев. Написание программ или сценариев для тестирова-
344 Часть III Создание профессиональных Ajax-приложений
ния, наверное, самая сложная часть работы, поскольку для ее выполнений надо знать, как пользователь будет взаимодействовать с приложением. Не исключено, что программа будет предназначена для нескольких категорий пользователей; в этом случае придется разработать несколько тестирующих сценариев. Используя одну программу, имитирующую некоего усредненного пользовааеля. можно упустить из виду важные детали, оказывающие существенное влияние на результаты работы. И наконец, готовую программу проверки нельзя рассматривать как нечто неизменное. При модификации приложения вполне может потребоваться внесение изменений в сценарий тестирования.
8.5. Резюме
Производительность любой компьютерной программы в основном определяется скоростью ее выполнения и объемом потребляемой памяти. В случае приложения Ajax мы имеем дело со специальной средой, имеющей мало общего с операционной системой и, тем более, с аппаратным обеспечением. Тем не менее, выбирая то или иное программное решение, мы можем существенно влиять на производительность программы.
Мы рассмотрели вопросы профилирования, причем использовали для создания профилей как библиотеки JavaScript, так и встроенные инструменты, например отладчик Venkman. Профилирование позволяет нам выявить узкие места системы, а также определить исходные точки отсчета для оценки влияния вносимых изменений. Сравнивая результаты профилирования до и после модификации кода, мы можем оценить влияние внесенных изменений на работу программы.
Мы также рассмотрели вопросы управления памятью и показали, как можно избежать утечки памяти при работе программы. При этом было уделено внимание как универсальным решениям, так и подходам, ориентированным на конкретные обстоятельства, например, использованию структуры DOM или работе с Internet Explorer.,Вы научились определять объемы памяти, потребляемой приложением, используя для этой цели инструменты, ориентированные на операционные системы Windows и Unix.
И наконец, мы рассмотрели пример, демонстрирующий важность принятия тех или иных решений при написании программы. Программные решения, примененные при разработке приложения, могут существенно влиять на потребление памяти и возхможность управления ею.
Работая над повышением производительности программы, никогда нельзя утверждать, что конечная цель достигнута, — на каждом этапе работы остается возможность некоторой оптимизации кода. Поэтому, оптимизируя Ajax-приложение, важно вовремя сказать '"достаточно" и не затрачивать большие усилия на получение скромных результатов. Для того чтобы принять решение об окончании работы по оптимизации, необходима информация о реальной производительности программы. В данной главе были рассмотрены инструменты, способные предоставить эту информацию разработчику.
Глава 8. Производительность приложения 345
8^6. Ресурсы
0 данной главе мы рассмотрели несколько инструментов.
•Drip, детектор утечки памяти, разработанный Джоэлом Веббером (Joel Webber) и ориентированный на Internet Explorer. Этот продукт доступен по адресу http://www.outofhanwell.com/ieleak/.
•Профилировщик Venkman (http://www.svendtofte.com/code/learning _yenkman/advanced).
•Process Explorer (http://www.sysinternals.com).
Официальная информация о проблемах использован ш памяти бра-
узером |
Internet Explorer и путях их устранения представлена по ад- |
ресу |
h t t p : / / m s d n . m i c r o s o f t . c o m / l i b r a r y / d e f a u l t . a s p ? u r l = / l i b r a r y / e n - |
us/lETechCol/dnwebgen/ie_leak__patterns .asp. Сведения о решениях, пред- ложенных Ричардом Корнфордом (Richard Cornford), можно получить, осуществив поиск посредством Google Groups. В качестве ключевых слов надо указать cornford j a v a s c r i p t fixCircleRef s () (полный URL слишком длинный, чтобы приводить его здесь).
Часть IV
Ajax в примерах
Х? данной части приводится пять завершенных проектов Ajax, демонстрирующих весь процесс создания неотразимых интерактивных элементов для вашего Web-приложения. В каждом случае мы последовательно разберем код одного примера и покажем, как он работает. Затем реструктуризируем код, чтобы вы могли использовать его в собственных проектах. Предлагаемые примеры охватывают весь спектр действий, доступных при использовании Ajax: от улучшения элементов форм до разработки завершенных порталов, сообщающихся как с вашими серверными процессами, так и со стандартными службами Интернета. При реализации кода серверной стороны мы специально задействовали смесь популярных языков программирования, поэтому в данном разделе вам встретятся PHP, Java, VB.Net и С# . В коде, который можно загрузить с Web-сайта данной книги, представлено несколько реализаций серверного кода, разобранного в данной главе. Не скучайте!
Динамические
Вэтой главе...
•Клиентский код JavaScript
•Серверный код VB.NET
•Формат обмена данными
•Реструктуризация для повторного использования
•Динамические окна выбора
