- •Введение
- •Несколько слов о книге
- •Глава 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
- •Формирование объектов
468 Часть IV. Ajax в примерах
Итак, мы сформировали все базовые функциональные возможности для рабочей системы портала, в том числе несколько элементов, которые классические Web-приложения просто не могли бы дать. Имеется еще несколько элементов, которые можно отнести к категории полезных, например, возможности добавления, удаления и переименования окон. Из-за ограниченного размера книги мы не будем их рассматривать. Впрочем, вы можете загрузить полный код приложения-портала, в котором можно добавлять, удалять, переименовывать и настраивать свойства окон, не покидая единственной страницы портала. Если у вас возникнут какие-либо вопросы, касающиеся приведенного кода, или вам потребуется более подробное его объяснение, вы всегда можете связаться с авторами через www.manning.com.
Разработанный код можно назвать грубым, однако он эффективен, так что мы можем демонстрировать работу его отдельных компонентов. Пожалуй, пришло время передать его группе, ответственной за реструктуризацию, и посмотреть, как связать все элементы воедино и облегчить повторное использование системы.
11.6. Реструктуризация
Как вы видели, концепция основанного на Ajax клиента портала, взаимо ] действующего с серверной частью портала, является довольно убедительной. ', В разделе, посвященном реструктуризации клиентской части кода мы рас- ] смотрим наш компонент как объект, выступающий в роли арбитра команд портала, отправляемых менеджеру портала на сервере. В процессе реструк- ] туризации мы постараемся выделить те части кода, которые могут со временем меняться, и максимально облегчить эти изменения. Поскольку портал ] представляет собой крулномодульный компонент, а мы в какой-то степени можем распоряжаться содержимым страницы, нас не ограничивает требо- ] вание не вмешиваться в HTML-разметку страницы (как в двух предыду- • щих проектах).
Однако, прежде чем рассматривать семантику клиентской части сценария, остановимся на контракте с сервером. Наша предыдущая реализация серверного кода была написана на Java, поэтому возможности аутентификации обеспечивал сервлетный фильтр(один сервлет возвращал конфигураЦИИ окна, второй — записывал конфигурации окна). Подобным образом, для добавления новых окон и удаления текущих мы создадим дополнительные автономные сервлеты. В Web-приложениях Java сервлеты можно достаточно гибко отобразить в URL; данная возможность определена в файле web.xml, содержащемся в загружаемом Web-архиве (.war). Например, функция Se- I lectServlet, возвращающая сценарий, определяющий исходные окна, была отображена в URL portalLogin.servlet.
Одним из достоинств инфраструктуры Ajax является слабая связь между клиентом и сервером. В нашем примере портала в качестве внутреннего интерфейса используется Java, но нам совсем не обязательно привязываться к таким особенностям Java, как сервлетные фильтры и гибкая перезапись URL. Альтернативная внутренняя архитектура может ис-
Глава 11. Улучшенный Web-портал Ajax |
469 |
пользовать диспетчер запросов, при которой единственный сервлет (страница РНР или ресурс ASP.NET) принимает все входящие запросы, а затем считывает параметр, задающий тип выполняемого действия (специфика передачи параметра определяется методом: GET или POST). Например, строка параметров для входа на портал может выглядеть как асtion=login&userid=userspassword=password. Используя Java, мы можем реализовать подход с помощью диспетчера запросов, присваивая определенный префикс URL, например .portal, сервлету диспетчера, что позволит записывать такие URL, как login.portal.
В реструктуризированном компоненте мы обобщим предположения относительно внутренней архитектуры, разрешив либо архитектуру диспетчера запросов, либо вариант с множественными адресами, использованный в реализации Java. Тем не менее от нас не требуется полная гибкость, поэтому мы предопределим набор команд, предположительно понятных для внутренней архитектуры портала и охватывающих регистрацию, отображение для пользователя окон портала, а также добавление и удаление окон из портала. Учитывая указанные изменения серверной части приложения, вернемся к реализации клиентской части.
Обсуждение реструктуризации портала начнем с переопределения контракта использования с точки зрения HTML-кода страницы, а затем приступим к реализации. Напомним, что введение HTML-страницы в сценарий портала происходит посредством регистрации, а точнее — с помощью кнопки входа в систему.
<input type="button" name="btnSub" value="login" onclick="LoginRequest(tquotelogintquote)">
Мы изменили обработчик события onclick, теперь это вызов функции, которая будет использовать наш компонент-портал. Предположим, что этот компонент обрабатывается с помощью сценария, выполняемого после загрузки. Характерный пример того, как это должно выглядеть, приведен в листинге 11.13.
Листинг 11.13. Создание портала и вход в систему
function createPortal() { myPortal = new Portal(
/ / О Основной URL портала tquoteportalManagertquote,
{
// © Необязательные параметры
messageSpanld: tquotespanprocessingtquote, urlSuffix: tquote.portaltquote }) ;
//© Вызвать для загрузки окон myPortal.loadPage(Portal-LOAD_SETTINGS_ACTION);
document.getElementById(tquoteusernametquote).focus{) ;
}
function login() { myPortal.login(document.getElementByld(tquoteusernametquote).value,
document.getElementById(tquotepasswordtquote).value);
470Часть IV. Ajax в примерах
Втакой семантике использования функция createportal (), которая должна вызываться сразу после загрузки страницы, создает экземпляр компонента портала. Первый ее аргумент — это основной URL серверного приложения портала О, второй предоставляет необязательные параметры, используемые для ее настройки под конкретный контекст 0. В данном случае мы сообщаем функции идентификатор элемента DOM, в который следует записывать сообщения о состоянии, и имя параметра запроса, который определит совершаемое действие. После создания на портале вызывается API loadPage, загружающий окна портала, если в сеансе сервера присутствует регистрационное имя пользователя ©. Если в систему никто не входил, сервер возвращает пустой сценарий, оставляя на экране только форму регистрации.
Функция login () представляет собой вспомогательную функцию, вызывающую метод login {) компонента портала, передающую в качестве аргументов имя пользователя и пароль. Согласно данному контракту обработчик событий onclick кнопки регистрации вызывает метод login () страницы.
<input type="button" name="btnSub" value="login" onclick="login()">
11.6.1. Определение конструктора
Разобравшись с использованием компонента с точки зрения страницы, займемся реализацией логики. Начнем с конструктора.
function Portal( baseUrl, options ) { this.basellrl = baseUrl;
this.options - options; this.initDocumentMouseHandler();
В качестве первого аргумента приведенный конструктор принимает URL средства управления порталом Ajax на сервере, а в качестве второго — объект опций, используемый при определении конфигураций. Напомним, что в первом варианте данного сценария использовался сервлетный фильтр и два сервлета, выполняющих внутреннюю обработку. Далее мы будем предполагать, что все запросы к внутренней части портала будет перехватывать один сервлет, или ресурс portalManager (см. листинг 11.13). Если нам требуется сконфигурировать портал с учетом внутренней архитектуры, не использующей единственный диспетчер запросов, мы можем передать конструктору различные аргументы, например:
myPortal = new Portal( tguotedatatquote,
{ messageSpanld: tquotespanProcessingtquote, urlSuffix: tquote.phptquote }
);
Таким образом, мы передадим основной URL "данных" и, поскольку в массиве опций определен параметр actionParam, добавим к пути URL команду с суффиксом .php, получая в результате URL, подобный data/login, php. Следовательно, мы получаем всю необходимую на данный момент гибкость. Вопрос превращения опций в URL будет рассмотрен в разделе 11.6.3. Сейчас же мы перейдем к следующему заданию. Итак, из по-
Гпава 11. Улучшенный Web-портал Ajax 471
следней строки конструктора следует необходимость адаптации библиотеки AjaxWindows.js.
11.6.2. Адаптация библиотеки AjaxWindows.js
Напомним, что реализация данного портала использует внешнюю библиотеку AjaxWindows.js для создания отдельных окон портала и управления их размером и положением на экране. В связи с этим нам, в частности, требуется адаптировать библиотеку для отправки запросов Ajax менеджеру портала, чтобы записать настройки после события mouseup. Мы отслеживаем данное действие потому, что теоретически им заканчиваются все операции перемещения и изменения размеров. Первое, что мы сделали для выполнения данной адаптации, — скопировали код библиотеки AjaxWindows.js и изменили в нем фрагмент, помещающий в документ обработчик событий mouseup. Если рассматривать библиотеку AjaxWindow.js как продукт стороннего производителя, то недостатки данного подхода очевидны. Мы отошли от кода чужой библиотеки, т.е. модифицировали исходный код и поведение библиотеки так, что они перестали быть совместимыми с версиями, поддерживаемыми авторами библиотеки. Если библиотека изменится, нам придется согласовывать его со своими изменениями при выходе каждой следующей версии. Мы ничего не сделали, чтобы изолировать место изменения и сделать его как можно более "безболезненным". Поэтому рассмотрим менее радикальный подход к адаптации и выясним, можно ли как-то исправить данную ситуацию. Напомним, что последняя строка конструктора выглядела следующим образом:
this . initDocumentMouseHandler();
Метод initDocumentMouseHandler () представляет оперативную адаптацию библиотеки AjaxWindows.js. Он, как и ранее, просто перезаписывает обработчик document.onmouseup, но делает это уже в нашем собственном коде. Теперь логика, требуемая для адаптации внутри метода портала handleMouseUp(), реализована в нашем методе (листинг 11.14).
Листинг 11.14. Адаптация обработчика AjaxWindows.js
initDocumentMouseHandler: function() { var oThis = this;
document.onmouseup = function() { oThis.handleMouseUp(); };
Ь
handleMouseUp: function() { bDrag = false;
bResize = false; intLastX = -1;
document.body.style.cursor = "default"; if { elemWin && bHasMoved )
this.saveWindowProperties(elemWin.id); bHasMoved = false;
Ь |
. |
__Л |
|
|
|
|
|
Это решение уже гораздо лучше, но это еще не все. Если библиотека AjaxWindows.js определяет обработчик mouseup в именованной, а не в ано-
472 Часть IV. Ajax в примерах
нимиой функции, то этот обработчик можно сохранить под другим именем и вызывать из нашего собственного обработчика. Таким образом, мы не будем дублировать логику, уже определенную в библиотеке AjaxWindows.js. Данный подход иллюстрируется в приведенном ниже коде.
function ajaxWindowsMouseUpHandler() { // logic here ...
}
document.onmouseup = ajaxWindowsMouseUpHandler;
Функция ajaxWindowsMouseUpHandler () представляет собой обратный вызов, определенный внешней библиотекой AjaxWindows.js. Как показано ниже, ее применение позволит сохранить определение метода и использовать его позже.
initDocumentMouseHandler: function() {
this.ajaxWindowsMouseUpHandler = aj axWindowsMouseUpHandler;
// О Сохранить |
нашу собственную ссылку |
v a r oThis = |
t h i s ; |
document.onmouseup = f u n c t i o n ( ) { oThis . handleMouseUp(); };
Ь
//© Вызвать библиотечную функцию handleMouseUp: function() {
this.aj axWindowsMouseUpHandler() ;
//© Добавить функциональные возможности Ajax if { elemWin && bHasMoved )
this.saveWindowProperties(eleraWin.id) ;
>,
Теперь наш метод handleMouseUp () не должен дублировать функциональные возможности библиотеки AjaxWindows.js. Необходимые возможности мы вызываем © посредством записанной ссылки О, а затем добавляем функциональные возможности Ajax ©. Если же обработчик mouseup библиотеки Ajax Windows в будущем изменится, то эти изменения не потребуют модификации нашего кода. Это уже более приятная ситуация с точки зрения управления изменениями. Разумеется, она предполагает, что подразумеваемый контракт с библиотекой не изменится — имеются в виду две глобальные переменные, elemWin и HasMoved. Поскольку в текущий момент библиотека определяет обработчик mouseup как анонимную функцию, мы по-прежнему можем записать ссылку на существующую функцию обработки события mouseup, используя следующую строку кода:
this.ajaxWindowsMouseUpHandler - this.document.onmouseup;
Таким образом, мы добиваемся того же результата, что и ранее, но теперь решение гораздо изящнее, поскольку в данной ситуации контракт гораздо слабее. Приведенное решение основывается на том, что мы включили наши эиблиотеки сценария в правильном порядке, т.е. библиотека AjaxWindows.js уже выполнила код, помещающий в документ обработчик mouseup. Кроме то- ^о, предполагается, что никакая другая библиотека не поместила в документ зругой обработчик событий mouseup или реализовала другой интерфейсный подход, подобный нашему.