- •Введение
- •Несколько слов о книге
- •Глава 1. Каким должен бытъ Web-интерфейс
- •Действия пользователя при работе с приложением
- •Накладные расходы при работе в сети
- •Асинхронное взаимодействие
- •Независимый и переходный образы использования
- •Четыре основных принципа Ajax
- •Браузер имеет дело с приложением, а не с содержимым
- •Сервер доставляет данные, а не содержимое
- •Реальное кодирование требует порядка
- •Применение богатых клиентов Ajax
- •Системы, созданные с использованием Ajax
- •Google Maps
- •Альтернативные технологии
- •Macromedia Flash
- •Java Web Start
- •Резюме
- •Ресурсы
- •Основные элементы Ajax
- •JavaScript изучался не зря
- •Определение внешнего вида с помощью CSS
- •Селекторы CSS
- •Свойства стилей
- •Простой пример использования CSS
- •Обработка DOM с помощью JavaScript
- •Поиск узла DOM
- •Создание узла DOM
- •Добавление стилей к документу
- •Свойство innerHTML
- •Асинхронная загрузка с использованием XML
- •Элементы IFrame
- •Объекты XmlDocument и XMLHttpRequest
- •Использование фуниции обратного вызова для контроля запроса
- •Жизненный цикл процедуры поддержки запроса
- •Отличия Ajax от классических технологий
- •Резюме
- •Ресурсы
- •Порядок из хаоса
- •Образы разработки
- •Реструктуризация и Ajax
- •Во всем надо знать меру
- •Реструктуризация в действии
- •Варианты применения реструктуризации
- •Несоответствие браузеров: образы разработки Fagade и Adapter
- •Управление обработчиками событий: образ разработки Observer
- •Повторное использование обработчиков событий: образ разработки Command
- •Обеспечение единственной ссылки на ресурс: образ разработки Singleton
- •"Модель-представление-контроллер "
- •Серверная программа Ajax, созданная без применения образов разработки
- •Реструктуризация модели
- •Разделение содержимого и представления
- •Библиотеки независимых производителей
- •Библиотеки, обеспечивающие работу с различными браузерами
- •Компоненты и наборы компонентов
- •Элементы, располагаемые на стороне сервера
- •Резюме
- •Ресурсы
- •Применение архитектуры MVC к программам различных уровней
- •Применение архитектуры MVC к объектам, присутствующим в среде браузера
- •Представление в составе Ajax-приложения
- •Отделение логики от представления
- •Отделение представления от логики
- •Контроллер в составе Ajax-приложения
- •Классические JavaScript-обработчики
- •Модель обработки событий W3C
- •Реализация гибкой модели событий в JavaScript
- •Модель в составе Ajax-приложения
- •Использование JavaScript для моделирования предметной области
- •Взаимодействие с сервером
- •Генерация представления на основе модели
- •Отражение объектов JavaScript
- •Обработка массивов и объектов
- •Включение контроллера
- •Резюме
- •Ресурсы
- •Программы, выполняемые на сервере
- •Создание программ на стороне сервера
- •N-уровневые архитектуры
- •Управление моделью предметной области на стороне клиента и на стороне сервера
- •Принципы создания программ на сервере
- •Серверные программы, не соответствующие основным принципам разработки
- •Использование архитектуры Model!
- •Использование архитектуры на базе компонентов
- •Архитектуры, ориентированные на использование Web-служб
- •Частные решения: обмен данными
- •Взаимодействие, затрагивающее только клиентскую программу
- •Пример отображения информации о планетах
- •Взаимодействие, ориентированное на содержимое
- •Взаимодействие, ориентированное на сценарий
- •Передача данных серверу
- •Использование HTML-форм
- •Использование объекта XMLHttpRequest
- •Управление обновлением модели
- •Резюме
- •Ресурсы
- •Создание качественного приложения
- •Отклик программы
- •Надежность
- •Согласованность
- •Простота
- •Как получить результат
- •Предоставление сведений пользователю
- •Поддержка ответов на собственные запросы
- •Обработка обновлений, выполненных другими пользователями
- •Создание системы оповещения
- •Основные принципы оповещения
- •Реализация базовых средств оповещения
- •Отображение пиктограмм в строке состояния
- •Отображение подробных сообщений
- •Формирование готовой системы
- •Предоставление информации в запросах
- •Информация о новизне данных
- •Простой способ выделения данных
- •Выделение данных с использованием библиотеки Scriptaculous
- •Резюме
- •Ресурсы
- •JavaScript и защита браузера
- •Политика "сервера-источника"
- •Особенности выполнения сценариев в Ajax-приложении
- •Проблемы с поддоменами
- •Взаимодействие с удаленным сервером
- •Взаимодействие с Web-службами
- •Защита конфиденциальной информации
- •Вмешательство в процесс передачи данных
- •Организация защищенного НТТР-взаимодействия
- •Передача шифрованных данных в ходе обычного HTTP-взаимодействия
- •Управление доступом к потокам данных Ajax
- •Создание защищенных программ на уровне сервера
- •Ограничение доступа к данным из Web
- •Резюме
- •Ресурсы
- •Что такое производительность
- •Скорость выполнения JavaScript-программ
- •Определение времени выполнения приложения
- •Использование профилировщика Venkman
- •Оптимизация скорости выполнения Ajax-приложения
- •Использование памяти JavaScript-кодом
- •Борьба с утечкой памяти
- •Особенности управления памятью в приложениях Ajax
- •Разработка с учетом производительности
- •Простой пример управления памятью
- •Как уменьшить объем используемой памяти в 150 раз
- •Резюме
- •Ресурсы
- •Сценарий двойной комбинации
- •Недостатки клиентского решения
- •Недостатки клиентского решения
- •Архитектура клиента
- •Разработка взаимодействия клиент/сервер
- •Реализация сервера: VB.NET
- •Написание кода сервера
- •Представление результатов
- •Применение каскадных таблиц стилей
- •Дополнительные вопросы
- •Запросы при выборе нескольких элементов
- •Переход от двойного связного выбора к тройному
- •Реструктуризация
- •Новый и улучшенный объект netContentLoader
- •Создание компонента двойного списка
- •Резюме
- •Глава 10. Опережающий ввод
- •Изучаем опережающий ввод
- •Типичные элементы приложений опережающего ввода
- •Google Suggest
- •Ajax как средство опережающего ввода
- •Структура серверной части сценария: С#
- •Сервер и база данных
- •Тестирование серверного кода
- •Структура клиентской части сценария
- •HTML
- •JavaScript
- •Обращение к серверу
- •Дополнительные возможности
- •Реструктуризация
- •День 1: план разработки компонента TextSuggest
- •День 3: включаем Ajax
- •День 4: обработка событий
- •День 5: пользовательский интерфейс всплывающего окна с предлагаемыми вариантами
- •Итоги
- •Резюме
- •Эволюционирующий портал
- •Классический портал
- •Портал с богатым пользовательским интерфейсом
- •Создание портала с использованием Java
- •Таблица пользователя
- •Серверная часть кода регистрации: Java
- •Структура регистрации (клиентская часть)
- •Реализация окон DHTML
- •База данных окон портала
- •Серверный код окна портала
- •Добавление внешней библиотеки JavaScript
- •Возможность автоматического сохранения
- •Адаптация библиотеки
- •Автоматическая запись информации в базе данных
- •Реструктуризация
- •Определение конструктора
- •Адаптация библиотеки AjaxWindows.js
- •Задание команд портала
- •Выводы
- •Резюме
- •Понимание технологий поиска
- •Классический поиск
- •"Живой" поиск с использованием Ajax и XSLT
- •Возврат результатов клиенту
- •Код клиентской части сценария
- •Настройка клиента
- •Инициализация процесса
- •Код серверной части приложения: РНР
- •Создание XML-документа
- •Создание документа XSLT
- •Объединение документов XSL и XML
- •Совместимость с браузером Microsoft Internet Explorer
- •Совместимость с браузерами Mozilla
- •Последние штрихи
- •Применение каскадных таблиц стилей
- •Улучшение поиска
- •Поддержка браузерами Opera и Safari
- •Использовать ли XSLT
- •Решение проблемы закладок
- •Реструктуризация
- •Объект XSLTHelper
- •Компонент "живого" поиска
- •Выводы
- •Резюме
- •Считывание информации из внешнего мира
- •Поиск XML-лент
- •Изучение структуры RSS
- •Богатый пользовательский интерфейс
- •Чтение лент
- •HTML-структура без таблиц
- •Гибкое CSS-форматироеание
- •Глобальный уровень
- •Предварительная загрузка средствами Ajax
- •Богатый эффект перехода
- •Правила прозрачности, учитывающие индивидуальность браузеров
- •Реализация затухающего перехода
- •Интеграция таймеров JavaScript
- •Дополнительные возможности
- •Введение дополнительных лент
- •Интеграция функций пропуска и паузы
- •Как избежать ограничений проекта
- •Обход системы безопасности браузеров Mozilla
- •Изменение масштаба приложения
- •Реструктуризация
- •Модель приложения
- •Представление приложения
- •Контроллер приложения
- •Выводы
- •Резюме
- •Отладчики
- •Для чего нужен отладчик
- •Средство Safari DOM Inspector для Mac OS X
- •Ресурсы
- •JavaScript — это не Java
- •Формирование объектов
Глава 8. Производительность приложения 305
дои машине, мы должны уметь заглянуть глубже спецификации DOM или стандарта ЕСМА-262, определяющего JavaScript, и учитывать реальные особенности конкретных браузеров. Если мы не будем знать "нижний уровень" иерархии программных средств, вряд ли нам удастся создать конкурентоспособный продукт. *
Если приложению требуется несколько секунд, чтобы обработать щелчок На кнопке, или несколько минут для пересылки содержимого формы, — это плохое приложение, независимо от того, насколько элегантными были решения разработчика. Если каждый раз, когда пользователь интересуется текущим временем, система запрашивает 20 Мбайт памяти, а потом освобождает лишь 15 Мбайт, пользователи вскоре откажутся от нее.
JavaScript не может обеспечить достаточно большое быстродействие, и как бы разработчики не усовершенствовали его, этот язык никогда не приблизится по скорости вычислений к языку С. Объекты JavaScript и элементы DOM требуют значительного объема памяти. Конкретные реализации браузеров далеки от совершенства и в ряде случаев напрасно расходуют память.
Производительность JavaScript-кода имеет большое значение для разработчиков Ajax-приложений, поскольку они вторгаются в ту область, которая еще не была исследована программистами. Объем JavaScript-кода в Ajaxпрограммах намного больше, чем в классическом Web-приложении. Время жизни объектов JavaScript в Ajax-программах больше по сравнению с классическими приложениями, потому что полное обновление Web-страниц происходит гораздо реже.
В двух следующих разделах мы обсудим две составные части производительности, а именно скорость выполнения и потребление памяти. Завершится эта глава рассмотрением примеров, демонстрирующих важность образов разработки при работе с Ajax-программами и со структурой DOM.
8.2. Скорость выполнения JavaScript-программ
В мире, в котором мы живем, ценится скорость. Срок окончания проектов истекает "еще вчера". (Если вы живете в другом мире, пришлите нам письмо, а еще лучше иммиграционную карту.) Быстродействующая программа предпочтительнее более медленной, при условии, конечно, что они обе выполняют поставленную задачу. Нас как разработчиков кода должно волновать, насколько быстро он работает и как улучшить его.
Существует правило, согласно которому скорость выполнения программы определяется скоростью самой медленной подсистемы. Мы можем измерить время работы всей программы, но результат вряд ли будет полезен. Гораздо лучше будет, если мы сможем выяснить быстродействие отдельных подсистем. Измерение скорости выполнения отдельных фрагментов кода обычно называют профилированием. Создание высококачественного кода, как и создание любого произведения искусства, никогда не завершается, а лишь останавливается на важных этапах. (Плохой код отличается от хорошего тем, что его создание остановилось на этапе, который был абсолютно не интересен.) Путем оптимизации всегда можно хоть немного ускорить выполнение
. U v l o in. ^изоание профессиональных Ajax-приложений
программы. Ограничивающим фактором при этом является не уровень ма. стерства, а имеющееся в наличии время. Выяснив с помощью инструмента профилирования "узкие места'1 нашего кода, мы можем сконцентрировать свои усилия для достижения наилучшего результата. Если же мы попытаемся оптимизировать код в процессе его написания, то вряд ли гарантированно получим хороший результат. "Узкие места" редко бывают именно там, где мы ожидаем их появления.
В данном разделе мы научимся измерять различными способами время выполнения кода. Кроме того, мы создадим простой профилировщик на JavaScript и попробуем поработать с уже существующим инструментом подобного назначения. Далее мы рассмотрим несколько простых программ и применим к ним профилировщик, чтобы выяснить наилучший способ их оптимизации.
8.2.1. Определение времени выполнения приложения
Самый простой инструмент измерения времени, имеющийся в нашем распоряжении, — это системный таймер, доступный из программы на JavaScript посредством объекта Date. Если мы создадим экземпляр Date, вызвав конструктор без параметров, то получим текущее время. Если одно значение Date вычесть из другого, разность будет представлена в миллисекундах. Пример использования объекта Date приведен в листинге 8.1.
Листинг 8.1. Измерение времени выполнения кода посредством Date
function myTimeConsumingFunctionf){ var beginning=new Date{);
// Важные и длительные вычисления
var ending=new Datef);
var duration=ending-beginning;
|
alert("this function took |
"+duration |
|
|
+"ms to do something interesting!"); |
||
_ J |
|
|
• |
Мы создаем объекты Date до начала и по окончании фрагмента кода, а затем определяем время выполнения этого фрагмента, вычисляя разность двух значений времени. В данном примере для представления информации о времени выполнения используется функция a l e r t (), но такое решение подходит лишь для простейших случаев. Обычно информацию о времени выполнения записывают в файл протокола, однако модель безопасности JavaScript запрещает доступ к локальной файловой системе. Самый простой подход, доступный в Aj axприложении, — это запись данных о профиле в память и последующее воспроизведение их в форме отчета.
Заметьте, что, для того, чтобы результаты измерений были максимально достоверными, код профилирования должен выполняться настолько быстро, насколько это возможно. Запись переменной в память осуществляется намного быстрее, чем создание дополнительных узлов DOM.
Глава 3. Производительность приложения 307
В листинге 8.2 представлена простая библиотека, реализующая секундомер, которую можно использовать для профилирования кода. В процессе Быполнения тестируемой программы информация о профиле хранится в памяти, а затем воспроизводится в виде отчета.
var stopwatch-new Object();
//Массив зарегистрированных таймеров stopwatch.watches=new Arrayt);
//Точка входа для клиентского кода stopwatch.getWatch=function(id,startNow){
var watch-stopwatch.watches[id] ; if {!watch){
watch=new stopwatch.Stopwatch(id);
}
if (startNow){ watch.start();
}
return watch;
}
// Конструхтор объекта, выполняющего функции секундомера stopwatch.StopWatch=function(id){
this.id-id; stopwatch.watches[idl-this; this.events-new Array(); this.objViewSpec-[
{name: "count", type: "simple"}, {name: "total", type; "simple"},
{name: "events", type: "array", inline:true}
]?
)
stopwatch.Stopwatch.prototype.startefunction(){ this.current=new TimedEvent(};
}
stopwatch.Stopwatch.prototype.stop=function(){ if (this.current){
this.current.stop();
this.events.append(this.current);
this.count++;
this.total+=this.current.duration;
this.current—null;
}
)
// Конструктор обработчика события измерения времени stopwatch.TimedEvent=function(){
this.start=new Date(); this.objViewSpec=[
{name: "start", type: "simple"}, {name: "duration", type: "simple"}
];
}
stopwatch.TimedEvent.prototype.stop=function(){ var stop=new Date О; this.duration=stop-this.start;
1
308 Часть fff Создание профессиональных Ajax-приложений
Рис. 8.1. Граф объектов библиотеки-секундомера. Каждая категория представляется объектом, который содержит предысторию событий. Все категории доступны посредством объекта s t o p w a t c h . w a t c h e s , существующего в единственном экземпляре
// Генератор отчета о |
профиле |
|
s t o p w a t c h . r e p o r t = f v m c t i o n ( d i v ) { |
||
v a r |
r e a l D i v = x G e t E l e m e n t B y I d ( d i v ) ; |
|
v a r |
report=new |
o b j v i e w e r . O b j e c t V i e w e r ( s t o p w a t c h . w a t c h e s , r e a l D i v ) I |
Наша система-секундомер поддерживает одну или несколько категорий, в каждой из которых существует одно активизированное событие и список предыдущих событий измерения времени. Когда клиентский код вызывает функцию stopwatch, s t a r t (), передавая ей в качестве параметра идентификатор, система создает для этой категории новый объект stopwatch либо повторно использует существующий объект. Затем клиент может многократно задавать команды секундомера s t a r t () и stop(). При каждом вызове функции stop() генерируется объект TimedEvent, в котором отмечается время запуска и длительность события. Если запустить секундомер несколько раз без промежуточных остановок, все обращения к s t a r t ( ) , кроме последнего, будут проигнорированы.
Результатом работы секундомера является граф объектов категорий stopwatch, каждая из которых содержит предысторию измеряемых событий. Пример подобного графа показан на рис. 8.1.
По окончании сбора данных визуализируется граф объектов. Функция render {) использует для автоматической генерации отчета библиотеку Object Viewer (см. главу 5). В качестве упражнения мы предлагаем читателям реализовать вывод данных в формате CSV, что позволит включать их в файл.
В листинге 8.3 приведен пример применения кода секундомера к некоторой функции, выполнение которой занимает длительное время
Глава 8 Производительность приложения |
J U S |
ЛИСТИНГ 8.3. Измерение времени с помощью бибпиотеки-секундомера
function myTimeConsuniingFvmctionO (
var vatch=stopwatch.getWatch("my time consuming function",true);
// Важные и длительные вычисления
|
watch.stop(); |
|
} |
] |
щ |
Код секундомера можно достаточно просто включить в программу, подлежащую тестированию. Затем следует определить категории для различных целей. В данном примере мы назвали категорию по имени функции.
Прежде чем переходить к новому разделу, проиллюстрируем изученный материал на конкретном примере. Оценим время выполнения программы Mousemat, рассмотренной нами в главе 4. В данной программе за перемещениями мыши следят два процесса. Один из них выводит информацию о текущих координатах в строке состояния браузера, а другой отображает точку, координаты которой соответствуют текущему расположению курсора мыши. Оба процесса представляют нам полезную информацию, но создают большую нагрузку на центральный процессор. Нам надо выяснить, какой из этих процессов потребляет больше процессорного времени.
С помощью библиотеки-секундомер а мы можем достаточно просто реализовать функции профилирования для данной программы. В листинге 8.4 показана модифицированная Web-страница, содержащая новый элемент DIV, в котором отображается отчет профилировщика. Кроме того, в текст сценария включены обращения к секундомеру.
Листинг 8.4. Документ mousemat. html со средствами профилирования
<htmlxhead>
<link rel='stylesheet' type='text/ess1 href='raousemat.ess1 /> <link rel=lstylesheet1 type='text/ess1 href='objviewer.ess1 /> <script type='text/javascript1 src='x/x_core.js'x/script> <script type='text/javascript' src=1extras-array.js*></script> <script type='text/javascript' src='styling.js'></script> <script type='text/javascript' src='objviewer . js'x/script> <script type='text/javascript' src='stopwatch.js'X/script> <script type='text/javascript' src='eventRouter.js'x/script> <script type='text/javascript'>
var cursor=null; window.onload=function(){
var watch=stopwatch.getWatch("window onload",true); var mat=docu-ment .getElementById( 'mouseraat' ) ; cursor=document,getElementById('cursor' ) ;
var mouseRouter=new jsEvent.EventRouter(mat,"onmousemove"); mouseRouter.addListener(writestatus); mouseRouter.addListener(drawThumbnail);
watch.stop();
}
function writeStatus(e)t
var watch=stopwatch.getWatch("write status",true); window.status=e.clientX+","+e.clientY;
