Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
107
Добавлен:
02.05.2014
Размер:
6.34 Mб
Скачать

206 Часть II. Основные подходы к разработке приложений

Он может оказаться полезен в двух случаях: при включении содержимого! предоставляемого внешними узлами, например, информации, получаемой от деловых партнеров, и при отображении данных, полученных от уже существующих приложений. В этих случаях использование формата HTML может обеспечивать высокую эффективность, и, как правило, не возникает необходимости преобразовывать информацию в другой вид. В качестве примера можно привести документы, содержащие справочную информацию. Использовать в приложениях Ajax подход, ориентированный на содержимое, целесообразно в тех случаях, когда в классических приложениях применяются всплывающие окна. Тем не менее данная архитектура имеет ограниченное применение. Причины этого нетрудно понять, зная ее недостатки.

Проблемы и ограничения

Поскольку архитектура, ориентированная на содержимое, мало отличается от привычных Web-страниц, ей присущи все ограничения данного подхода. Документ в элементе I Frame изолирован от остальной части страницы, в которую он включается. В результате информация на экране выглядит фрагментированной. Правда, при размещении содержимого возможны различные варианты. Несмотря на то что I Frame представляет собой прямоугольное окно, содержащее дочерний документ, для него может быть задан прозрачный фон, в результате чего содержимое окна будет смешиваться с содержимым родительского документа.

У некоторых разработчиков может возникнуть мысль использовать данный механизм для доставки динамических частей страницы, но в этом случае могут возникнуть проблемы при работе с элементами IFrame. Каждый элемент I Frame поддерживает собственный контекст, и объем вспомогательного кода, необходимого для обеспечения взаимодействия содержимого IFrame с родительским документом, может оказаться значительным. При взаимодействии со сценариями в других фреймах возникают еще более серьезные проблемы. Мы вернемся к этому вопросу при рассмотрении подхода, ориентированного на сценарий.

Нельзя также забывать о проблемах, связанных с практичностью традиционных Web-приложений. Во-первых, каждый запрос на получение содержимого предполагает повторное получение статических данных. Во-вторых, несмотря на то, что при обновлении данных в основном документе автоматически принимаются меры для подавления мерцания, в элементе IFrame этот эффект может присутствовать. Мерцания, правда, можно избежать, реализовав дополнительный код для отображения сообщения "поверх" фрейма.

Итак, в качестве первого термина нашего словаря, описывающего запросы к серверу Ajax, мы записали понятие "ориентированный на содержимое". Применимость данного подхода ограничена, однако учесть такую возможность все же необходимо. Существует ряд задач, которые нельзя удовлетворительно решить в рамках подхода, ориентированного на содержимое. В качестве примера таких задач можно привести обновление части компонента, например, отдельной пиктограммы или. строки таблицы. Единственный способ решить такую задачу — передавать JavaScript-код.

Глава 5. Роль сервера в работе Ajax-приложения 207

Варианты применения подхода, ориентированного на содержимое

До сих пор, рассматривая архитектуру, ориентированную на содержимое, мы предполагали, что для получения данных используется элемент IFrame. В качестве альтернативного решения можно рассмотреть генерацию фрагмента HTML-кода, как реакцию на асинхронный запрос, и присвоение ответа свойству innerHTML элемента DOM текущего документа. Более подробно мы рассмотрим данное решение в главе 12.

5.4.4. Взаимодействие, ориентированное на сценарий

Когда мы организуем передачу с сервера JavaScript-файла и выполнение этого файла в среде браузера, мы можем утверждать, что решаемая нами задача нетривиальна. Если JavaScript-код, предназначенный для передачи, генерируется программой, то выполняемые действия еще более сложны. Традиционно клиентские и серверные программы обмениваются данными друг с другом. Передача по сети исполняемого кода обеспечивает дополнительную гибкость. Поддержка мобильного кода реализована лишь в инструментах, предназначенных для создания корпоративных приложений, например Java и .NET. Для этой цели специально предусмотрены технологии RMI, Jini, и .NET Remoting Framework. Разработчики же простых Web-приложений решают подобные задачи постоянно! Логично предположить, что Ajax расширит наши возможности по работе с мобильным кодом.

Общие сведения

В классическом Web-приложении HTML-код и JavaScript-сценарий обычно располагаются в одном файле, и сценарий специально ориентирован на работу с конкретной Web-страницей. Используя Ajax, мы можем загружать сценарии и Web-страницы независимо друг от друга, а это дает возможность модифицировать страницу различными способами, в зависимости от полученного сценария. Коды, составляющие клиентскую часть приложения, могут быть существенно расширены в процессе работы. Это не только предоставляет новые возможности, но и, как вы вскоре увидите, создает проблемы. На рис. 5.8 условно показана архитектура, ориентированная на сценарии.

Первое преимущество данного подхода по сравнению с архитектурой, ориентированной на содержимое, состоит в том, что активные действия передаются элементам "второго плана", что предотвращает мерцание изображения.

Особенности генерируемых сценариев определяются потребностями клиентского уровня. Как и для любой задачи, предполагающей генерацию кода, Успех ее решения зависит от того, удастся ли добиться, чтобы сгенерированный код был простым и использовал существующие библиотеки. Библиотеки могут либо передаваться вместе с новым кодом, либо постоянно присутствовать на стороне клиента.

В любом случае данная архитектура предполагает тесную связь между Уровнями. Для генерации кода на стороне сервера необходимо иметь информацию об API на стороне клиента. При этом возникают две проблемы. Вопервых, модифицируя клиентский или серверный код, можно случайно на-

208 Часть II. Основные подходы к разработке приложений

Рис. 5.8. Архитектура Ajax-приложения, ориентированная на сценарии. Клиентская программа передает серверу запрос на получение фрагмента JavaScript-кода. Полученный код интерпретируется. На стороне клиента обеспечиваются точки входа для сгенерированных сценариев, что позволяет сценарию управлять клиентской программой

рушить взаимодействие. Модульный подход к разработке и использование образа разработки Fagade могут несколько снизить подобную опасность. Вовторых, поток JavaScript-кода ориентирован на конкретного клиента, и организовать его повторное использование намного сложнее, чем, например, потока XML-данных. Следует, однако, заметить, что пригодность к повторному использованию не всегда бывает важна.

Теперь вернемся к примеру отображения информации о планетах Солнечной системы. В листинге 5.4 показан простой API для отображения окон с данными.

Листинг 5.4. Функция showPopupO и вспомогательный код var offset=8;

function showPopup(name,description) { var win=new ScriptlframePopup

(name,description,offset,offset,320,320);

offset+=32;

}

function ScriptlframePopup(name,description,x,y,w,h){ var bod=document.createElement("div"); document.body.appendChild(bod);

this.contentDiv=document.createElement("div");

this.contentDiv.className="winContents";

this.contentDiv.innerHTML=description; bod.appendChild(thi s.contentDiv);

this.win=new windows.Window(bod,name,x,y,w,h);

}

_

Мы определили функцию showPopup (), которая получает два параметра и создает объект окна. В листинге 5.5 приведен пример сценария, вызывающего эту функцию.

Глава 5. Роль сервера в работе Ajax-приложения 209

Листинг 5.5. Содержимое файла script_earth. js var name='earth'; var description="A small blue planet near the outer rim of the galaxy,"

+"third planet out from a middle-sized sun."; showPopup (name,description);

В данном случае мы подготавливаем параметры (имя и описание) и вызываем функцию. Для того чтобы такой вызов стал возможен, мы должны загрузить сценарий с сервера и заставить браузер выполнить его. Существуют два способа, позволяющие сделать это. Рассмотрим каждый из них.

Загрузка сценариев в элементы IFrame

Если мы загрузим JavaScript-код, используя HTML-дескриптор <script>, сценарий будет автоматически обработан интерпретатором. С этой точки зрения элемент IFrame не отличается от обычного документа. Мы можем определить метод showlnf о () для создания IFrame и загрузить в него сценарий.

f u n c t i o n showlnfо(event){ var p l a n e t = t h i s . i d ;

var s c r i p t U r l = " s c r i p t _ " + p l a n e t + " . h t m l " ; v a r

d a t a f r a m e = d o c u m e n t . g e t E l e m e n t B y I d ( ' d a t a f r a m e ' ) ; i f ( ! d a t a f r a m e ) {

d a t a f r a m e = d o c u m e n t . c r e a t e E l e m e n t ( " i f r a m e " ) ; d a t a f r a m e . c l a s s N a m e = ' d a t a f r a m e ' ;

d a t a f r a m e . i d = ' d a t a f r a m e ' ; d a t a f r a m e . s r c = s c r i p t U r l ;

d o c u m e n t . b o d y . a p p e n d C h i l d ( d a t a f r a m e ) ; ) e l s e { d a t a f r a m e . s r c = s c r i p t U r l ;

}

}

Средства для обработки DOM уже знакомы вам. Если мы используем для загрузки сценария невидимый элемент IFrame, то можем сосредоточить внимание на самом сценарии, поскольку все остальные действия выполняются автоматически. Включим наш пример сценария в HTML-документ, как показано в листинге 5.6.

Листинг 5.6. Содержимое файла script earth.html

<html>

<head> <script type='text/javascript'

src='script_earth.js'> </script> </head> <body> </body>

</html>

_

Если мы попытаемся загрузить этот код, он не будет работать, так как IFrame формирует собственный JavaScript-контекст и не может непосредственно обратиться к API, определенному в основном документе. Если в сценарии выполняется приведенное ниже выражение, браузер ищет функцию showPopup () в контексте IFrame.

showPopup (name, description);

210 Часть II. Основные подходы к разработке приложений

В простых ситуациях, подобных рассматриваемой, при наличии двух контекстов мы можем указать перед вызовом идентификатор top, формируя тем

самым ссылку на документ верхнего уровня. top.showPopup(name,description);

Если лее элемент I Frame содержится в составе другого элемента I Frame или если мы хотим запустить наше приложение в системе фреймов, ситуация усложняется.

В загружаемом нами сценарии используется функциональный подход. Если нам понадобится создать в I Frame экземпляр объекта, мы столкнемся с новой проблемой. Предположим, что в файле Planetlnfo. js определен объект Planetlnfo, к которому наш сценарий обращается следующим образом:

var pinfo=new PlanetInfo(name,description);

Для того чтобы такое обращение было возможно, мы должны импортировать Planetlnfo. js в контекст IFrame, указав дополнительный дескриптор <script>.

<script type='text/javascript1 src='Planetlnfo.j s'></script> <script type='text/j avascript'>

var pinfo=new Planetlnfo(name,description); </script>

Объект Planetlnfo, созданный в I frame, будет вести себя так же, как и объект, созданный во фрейме верхнего уровня, но они имеют разные прототипы. Если впоследствии мы удалим элемент IFrame, а документ верхнего уровня сохранит ссылку на объект, созданный в составе IFrame, то последующие обращения к методам объекта невозможны. Более того, оператор instanceof даст совершенно неожиданные результаты (табл. 5.1).

Импортирование определения объекта в различные контексты — не такая простая задача, как это может показаться на первый взгляд. Решить проблему можно, определив в API документа верхнего уровня фабричный метод. Например:

function createPlanetlnfo(name,description){ return new Planetlnfo(name,description);

}

Глава 5. Роль сервера в работе Ajax-приложения 211

При этом исчезает необходимость иметь ссылку на собственную версию объекта Planetlnfo.

<script type='text/javascript'>

var pinfo=createPlanetInfo(name,description); </script>

функция showPopupO в листинге 5.4 представляет собой фабрику для объекта ScriptlframePopup.

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

Загрузка сценариев с использованием XMLHttpRequest и eval()

Подобно многим языкам сценариев, JavaScript содержит функцию eval (), которая позволяет передавать интерпретатору JavaScript произвольный текст. Некоторые считают, что использование функции eval () замедляет работу приложения. Это действительно так, если функция eval () систематически вызывается для малых сценариев. Однако, поскольку эта функция доступна нам, мы можем использовать ее для выполнения кода, загруженного с помощью объекта XMLHttpRequest. Если функцию eval () вызывать относительно редко для сценариев большого объема, она обеспечивает приемлемую производительность.

Переписав наш пример для работы с функцией eval (), получим следующий код:

function showlnfo(event){ var planet=this.id;

var scriptUrl="script_"+planet+".js";

new net.ContentLoader(scriptUrl, evalScript); ) function evalScript(){

var script=this.req.responseText; eval(script); }

Теперь метод showlnfoO использует объект XMLHttpRequest (помещенный в класс ContentLoader) для получения сценария с сервера; причем необходимость включать его в состав HTML-страницы отпадает. Вторая функция, evalScript(), представляет собой функцию обратного вызова; ссылка на нее передается ContentLoader. Когда функция evalScript () получает управление, может быть прочитано значение свойства responseText, принадлежащего объекту XMLHttpRequest. Весь сценарий выполняется не в отдельном контексте IFrame, а в контексте текущей страницы.

Теперь мы описали новый термин — "ориентированный на сценарий". Заметим, что данный подход допускает две реализации; посредством IFrame или eval (). Сравним подход, ориентированный на сценарий, с подходом, ориентированным на содержимое.

212 Часть II. Основные подходы к разработке приложений

Проблемы и ограничения

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

Очевидным недостатком является взаимная зависимость клиентского и серверного кода. JavaScript-код, получаемый от сервера, крайне трудно использовать в другом контексте, поэтому приходится специально разрабатывать программы для Ajax-клиентов. После начала эксплуатации изменить API клиента очень сложно.

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

5.4.5. Взаимодействие, ориентированное на данные

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

Общие сведения

Иногда бывает необходимо организовать использование данных, полученных Ajax-клиентом, другими программами, например, интеллектуальными клиентами Java или .NET, либо программным обеспечением мобильных телефонов или КПК. В этих случаях JavaScript-инструкции плохо применимы; более уместным был бы "нейтральный" формат данных.

При взаимодействии, ориентированном на данные, сервер передает лишь данные, которые обрабатываются не интерпретатором JavaScript, а самой клиентской программой. Архитектура, ориентированная на данные, условно показана на рис. 5.9.

Большинство примеров, приведенных в данной книге, создано в рамках подхода, ориентированного на данные. Наиболее очевидный формат данных — XML, но возможны также и другие форматы.

Использование XML-данных

В настоящее время формат XML применяется очень часто. Среда браузера, в которой выполняется Ajax-приложение и, в частности, объект XMLHttpRequest, предоставляет средства поддержки XML-данных. Если объект XMLHttpRequest получает ответ типа application/xml или text/xml, он оформ-

Глава 5. Роль сервера в работе Ajax-приложения 213

Клиент

Рис. 5.9. В системе, ориентированной на данные, сервер в ответ на запрос возвращает поток низкоуровневых данных (представленных, например, в формате XML). Эти данные обрабатываются на уровне клиента и используются для обновления клиентской модели и (или) пользовательского интерфейса

ляет его в виде структуры DOM (Document Object Model). В листинге 5.7 приведен код приложения, отображающего данные о планетах, адаптированный для работы с информацией в формате XML.

Листинг 5.7. Содержимое файла DataXMLPopup. js

|p

var offset=8;

function

 

showPopup(name,description){

 

var win=new

DataPopup(name,description,offset,offset,320,320);

 

offset+=32;

 

 

}

 

 

function

DataPopup(name,description, x, y,w,h){ var bod=document.createElement("div"); document.body.appendChild(bod);

this.contentDiv=document.createElement("div");

this.contentDiv.className="winContents";

this.contentDiv.innerHTML=description;

bod.appendChild(this.contentDiv); this.win=new windows.Window(bod,name,x,y,w,h);

}

function showlnfo(event){ var planet=this.id;

var scriptUrl=planet+".xml";

new net.ContentLoader(scriptUrl,parseXML);

}

function parseXML(){ var name="";

var descrip="";

var xmlDoc=this.req.responseXML;

var elDocRoot=xmlDoc.getElementsByTagName("planet")[0]; if (elDocRoot){

attrs=elDocRoot.attributes;

214 Часть II. Основные подходы к разработке приложений

name=attrs.getNamedItem("name").value;

var ptype=attrs.getNamedItem("type").value; if (ptype){

descrip+="<h2>"+ptype+"</h2>";

}

descrip+="<ul>";

for(var i=0;i<elDocRoot,childNodes.length;i++){ elChild=elDocRoot.childNodes[i];

if (elChild.nodeName=="info"){

descrip+="<li>"+elChild.firstChild.data+"</li>(BBSS)n";

}

}

descrip+="</ul>";

}else{

alert("no document");

}

top.showPopup(name,descrip);

}

Функция showinfo() обращается к объекту XMLHttpRequest, содержащемуся в составе ContentLoader, и объявляет в качестве функции обратного вызова parseXML (). В данном случае функция обратного вызова сложнее, чем evalScript(), которую мы рассматривали в разделе 5.6.3. Это связано с тем, что нам необходимо поддерживать навигацию по структуре DOM, извлекать данные и вызывать метод showPopup. В листинге 5.8 содержится пример ответа, сгенерированного сервером в формате XML.

Листинг 5.8. Содержимое файла earth.xml <planet name="earth" type="small">

<info id="a"

author="dave"

date="26/05/04">

Earth is a

small planet,

third from the sun

</info>

 

 

<info id="b"

author="dave"

date="27/02/05">

Surface coverage of water is roughly two-thirds

</info>

 

 

<info id="c"

author="dave"

date="03/05/05">

Exhibits a remarkable diversity of climates and landscapes

</info>

 

 

</planet>

 

_

Существенным преимуществом XML является тот факт, что информация, представленная в этом формате, естественным образом структурирована. Так, в данном примере содержится ряд дескрипторов <info>, которые функция parseXML () преобразует в маркированный HTML-список.

Глава 5. Роль сервера в работе Ajax-приложения 215

Таким образом, благодаря использованию XML нам удалось разделить уровни сервера и клиента. Код клиента и сервера можно изменять, независимо один от другого, при условии, конечно, что они будут поддерживать формат документа. Однако подход, ориентированный на сценарий, имел очень важное преимущество: вся работа выполнялась JavaScript-интерпретатором. Рассмотрим решение, предполагающее использование JSON. Оно позволяет объединить преимущества обеих архитектур.

Использование JSON-данных

Имя для объекта XMLHttpRequest было выбрано не совсем удачно. На самом деле он может принимать не только XML, но и любую текстовую информацию. Для передачи данных Ajax-клиенту очень удобен формат JSON (JavaScript Object Notation), так как он позволяет представить в компактном виде граф объектов JavaScript. В листинге 5.9 показано, как молено адаптировать пример приложения, предоставляющего информацию о планетах, для использования JSON.

Листинг 5.9. Содержимое файла DataJSONPopup. j s function showlnfo(event)

{

var planet=this.id;

var scriptUrl=planet+".json";

new net.ContentLoader(scriptUrl,parseJSON);

}

 

function parseJSON()

,

{

 

var name="";

 

var descrip="";

 

var jsonTxt=net.req.responseText;

 

var jsonObj=eval("("+jsonTxt+")");

 

name=j sonObj.planet.name

 

var ptype=jsonObj.planet.type;

 

if (ptypeH

 

descrip+="<h2>"+ptype+"</h2>";

 

)

 

var infos=jsonObj.planet.info;

 

descrip+="<ul>";

 

for(var i in infos){

 

descrip+="<li>"+infos[i]+"</li>\n";

 

}

descrip+="</ul>";

top.showPopup(name,descrip);

-J

 

 

 

Здесь мы также загружаем данные с помощью ContentLoader. Функцией обратного вызова в этом случае является parse JSON (). Текст ответа представляет собой JavaScript-выражение, поэтому мы можем создать граф объектов путем вызова eval ().