Поиск на сайте: Расширенный поиск


Новые программы oszone.net Читать ленту новостей RSS
EF StartUp Manager - программа для слежением за программами, которые запускаются вместе с загрузкой операционной системы...
EF Mailbox Manager - программа для периодической проверки неограниченного количество аккаунтов электронной почты. Вы мож...
Программа позволяет узнать, чем занято место на жестком диске. Сканирует папки очень быстро и выводит наглядную диаграмм...
HashMyFiles - это маленькая утилита, которая предоставляет возможность рассчета контрольных сумм MD5 и SHA1 для одного и...
Файловый менеджер с очень малыми системными требованиями, но тем не менее с большими возможностями. Программа имеет ориг...
OSzone.net Microsoft Разработка приложений HTML5 Создание приложений HTML5: Урок истории — History API RSS

Создание приложений HTML5: Урок истории — History API

Текущий рейтинг: 4 (проголосовало 2)
 Посетителей: 1207 | Просмотров: 1462 (сегодня 0)  Шрифт: - +

Под историей в контексте веб-браузера обычно подразумевают кнопку «назад», и этой историей никогда не было легко управлять. Это стало даже труднее, когда в веб-приложениях начали широко использовать AJAX и типичный веб-сайт превратился в веб-приложение с богатой функциональностью. А когда поддержку JavaScript начали включать по умолчанию (а не выключать, как раньше), появились одностраничные веб-приложения, интенсивно использующие JavaScript на клиентской стороне. Но хорошего способа работать с клиентским хранилищем, базами данных или даже сохранением состояния при переходе между страницами не было.

Однако теперь HTML5 берет на себя управление состоянием на клиентской стороне и вводит целый набор новых API, включая такие спецификации, как IndexedDB, Local Storage и History API, на которых я и сосредоточусь в этой статье.

Если в двух словах, то цель History API — обеспечить JavaScript-приложениям с богатой функциональностью более широкие возможности не только в навигации по истории сеанса, но и в управлении состоянием, а также в полноценной поддержке URL. Это означает, что с помощью нескольких простых API-вызовов и событий вы можете осуществлять навигацию, сохранять данные состояния в стек сеанса и извлекать их оттуда, чтобы влиять, например, на то, как функционирует кнопка «назад», в то же время поддерживая четкую структуру URL.

Проблема

Работая над любым решением, важно понимать проблему. History API — это не только какой-то волшебный способ сохранения состояния страниц, когда пользователь щелкает кнопки «назад» и «вперед». На самом деле все гораздо глубже. С точки зрения пользователя, в любом браузере есть две основные вещи: URL и кнопки навигации (вперед и назад). Вместе они позволяют запрашивать или переходить по сериям документов в Интернете.

Хотя с годами эти вещи остаются в основном неизменными, с появлением AJAX «за кулисами» многое поменялось. Что когда-то было сериями документов, стало просто файлом с AJAX-вызовами, выполняемыми на внутреннем уровне. Это было замечательно: мы смогли обеспечить более удобный UI с чуточкой асинхронности, не говоря уже о выигрыше в производительности. Но это же создало некоторые проблемы: стало гораздо труднее отслеживать URL, чтобы, например, знать, куда следует направить браузер при щелчке кнопки возврата или обновления.

Не думаю, что можно преувеличить важность URL. Они всюду: пользователи создают из них избранное, поисковые системы индексируют их, а компании продают и покупают их. Как управлять ими в постоянно меняющемся мире Web?

Введите символ решетки (#) и решетку с восклицательным символом (#!). Браузеры интерпретируют все, что находится за #, как идентификатор фрагмента URL и никогда не отправляют это на сервер. Изначально решетка задумывалась как способ связывания анкерных тегов внутри страницы, но стала использоваться для операций, относящихся к AJAX. Однако на ранних этапах развития AJAX адреса URL, содержащие только решетку, не индексировались поисковыми системами. Эту задачу решили применением адреса с восклицательным знаком (hashbang), что позволило веб-приложениям использовать и модифицировать URL, а поисковым системам — индексировать его без отправки запроса страницы обратно на сервер. URL, содержащие адреса с восклицательным знаком (hashbangs), например twitter.com/#!replies, стали важными с появлением веб-приложений.

Новый History API

Хотя все это было большим шагом вперед, на самом деле необходимая поддержка в браузерах отсутствовала. Веб-приложения просто играли в скриптовые игры, чтобы это работало. Новый History API сфокусирован на том, чтобы у веб-приложений появилась возможность «управлять состоянием», что подразумевает отслеживание последовательности документов, образующих историю сеанса. Это позволяет вам просматривать историю сеанса и сохранять данные состояния, а также корректно обрабатывать URL. Приступая к исследованию этого API, давайте посмотрим на определение этого интерфейса в соответствующей спецификации W3C (bit.ly/MU89iZ):

interface History {
  readonly attribute long length;
  readonly attribute any state;
  void go(optional long delta);
  void back();
  void forward();
  void pushState(any data,
    DOMString title,
    optional DOMString url);
  void replaceState(any data,
    DOMString title,
    optional DOMString url);
};

Ничего сложного в этом API нет.

Чтобы получить представление о том, как он работает, начнем с простой навигации между документами. Допустим, у вас есть три документа (a.cshtml, b.cshtml и c.cshtml), и вы хотите перемещаться между ними. В типичном случае вам следовало бы просто создать анкерный тег <a href="http://foo.com/a.cshmtl">goto a</a>, который щелкал бы пользователь, заставляя браузер выполнять свой обычный цикл работы со страницей и сервером. Когда пользователь щелкает ссылки по этому веб-сайту, он создает так называемую историю сеанса.

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

AJAX решает часть проблемы, позволяя запрашивать только части страницы, сокращая количество запросов полной страницы, и вручную обновлять URL до примерно такой формы: http://foo.com/#!a или http://foo.com/#a. Чтобы сделать нечто подобное с помощью History API, вы вызываете window.history.pushState(state, title, url), передавая любое состояние, которое вам нужно сохранить, а также название страницы и отображаемый URL. Заметьте, что от вас не требуется использовать URL с решеткой или восклицательным знаком; вы можете указывать просто http://foo.com/a, даже если «a» физически не существует.

Вызывая pushState, вы создаете историю сеанса данного пользователя. Этот пользователь может перемещаться по этой истории так, как ему нужно, и все будет работать ожидаемым образом. Нажав кнопку «назад», он переходит по предыдущему URL, а URL, которые были там, существуют по-прежнему.

Кроме того, у вас есть точки подключения, которые позволяют вам переходить по истории сеанса пользователя и осуществлять поиск в ней. Вы можете динамически перемещать пользователя вперед и назад по стеку — так, будто это сам пользователь щелкает кнопки «вперед» и «назад».

Пример из реальной жизни

Давайте перейдем к конкретике на примере из реальной жизни. Я провожу конференцию по технологиям That Conference (thatconference.com). На конференции много докладчиков, но я не хочу создавать по странице для каждого из них. Я предпочел динамически создавать страницу для каждого выступающего, которая кажется настоящей. Я легко сделал это с помощью History API.

Не думаю, что можно преувеличить важность URL.

Как и в любом веб-приложении, интенсивно использующем скрипты, мне нужны были данные. К счастью, в That Conference есть простой REST API, который можно вызывать для получения докладчиков так: thatConference.com/api/person. Это вызов вернет массив докладчиков за указанный год в формате JSON или XML. Один из элементов этого массива показан на рис. 1.

Рис. 1. Профиль докладчика

<PersonViewModel>
  <FirstName>Scott</FirstName>
  <LastName>Hanselman</LastName>
  <Company>Microsoft</Company>
  <Bio>
    My name is Scott Hanselman. I work out of my home office
for Microsoft as a Principal Program Manager, aiming to spread
good information about developing software, usually on the
Microsoft stack. Before this I was the Chief Architect at
Corillian Corporation, now a part of Checkfree, for 6-plus
years. I was also involved in a few Microsoft Developer things
for many years like the MVP and RD programs and I'll speak
about computers (and other passions) whenever someone
will listen.
  </Bio>
  <Twitter>shanselman</Twitter>
  <WebSite>http://www.hanselman.com</WebSite>
  <Gravatar>/Images/People/2012Speakers/ScottHanselman.png
  </Gravatar>
</PersonViewModel>

Данные бесполезны, если нет способа их увидеть. Мне нужно подготовить простой шаблон разметки, который я смогу использовать для динамического создания страницы для каждого докладчика. С этой целью я задействую инфраструктуру Knockout (knockoutjs.com). Knockout — это JavaScript-библиотека, помогающая разработчикам применять декларативные привязки с шаблоном Model-View-ViewModel (MVVM). От вас не требуется использовать Knockout для History API, но я все же сделаю это — будет кое-что интересное.

Так как страница каждого докладчика одна и та же, я определю простой шаблон разметки в Knockout. Мне нужно создать блок script и сообщить инфраструктуре, как его потом заполнять:

<script type="text/html" id="person-template">
  <div>
    <p>
      <strong>Name:</strong>
        <span data-bind="text: FirstName"></span>
        <span data-bind="text: LastName"></span>
    </p>
    <p>Company: <strong data-bind="text: Company"></strong></p>
    <p>Bio: <strong data-bind="text: Bio"></strong></p>
  </div>
</script>

Далее нужно заполнить шаблон. Для этого я вызываю ko.applyBindings(someData), и Knockout творит свою магию над любым объектом, передаваемым в applyBindings. Благодаря этому у меня появляется базовый механизм для передачи объекта докладчика и заполнения разметки его данными.

Однако моя цель немного сложнее. На самом деле мне нужно, чтобы пользователь мог пролистывать серии страниц — читать книгу докладчиков, если хотите. Вот что должно происходить при первой загрузке страницы.

  1. Получаем JSON, представляющий докладчиков.
  2. Связываем первый элемент в массиве с шаблоном Knockout как вариант по умолчанию.
  3. Вызываем window.pushState, передавая соответствующие аргументы.

О первых двух этапах я уже рассказывал, так что поговорим о pushState. Вызывая window.pushState, вы создаете элемент в истории сеанса пользователя. Я буду передавать при вызове pushState три элемента:

  • state — в моем случае это элемент массива, связанный с шаблоном Knockout;
  • title — это название страницы, состоящее из полного имени докладчика;
  • url — URL страницы; в данном случае это будет нечто вроде thatconference.com/speakers/speaker/SpeakerFullName.

Всю эту логику я обернул в метод bind:

function bind (speakerID) {
  var speakerVM = new speakerViewModel(speakerID);
  var fullName = speakers[speakerID].FirstName +
    speakers[speakerID].LastName
  window.history.pushState(speakerVM, fullName,
    "/speakers/" + fullName);
  ko.applyBindings(speakerVM);
}

Теперь добавим пару кнопок в книгу докладчиков, чтобы пользователь мог перемещаться между этими докладчиками:

<button id="prevSpeaker">previous speaker</button>
<button id="nextSpeaker">next speaker</button>

Конечно, мне потребуется пара обработчиков событий для кнопок nextSpeaker и prevSpeaker. Чтобы отслеживать, какой докладчик должен быть следующим, я создам простой счетчик, изменяемый в процессе навигации. Значение счетчика передается в метод bind:

var counter = 0;
$('#nextSpeaker').click( function () {
  counter = counter + 1;
  bind(counter);
});
$('#prevSpeaker').click( function () {
  counter = counter - 1;
  window.history.back();
});

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

События

При нажатии кнопки «назад» (или через скрипт) вызывается windows.history.back, срабатывает событие onpopstate. Это точка подключения в процесс перемещения назад по истории сеанса пользователя. Событие onpopstate содержит данные состояния, ранее переданные в pushState; в нашем случае это один докладчик.

Теперь нужно извлечь эти данные и сообщить Knockout о необходимости их связывания:

window.onpopstate = function (event) {
  console.log('onpopstate event was fired');
  ko.applyBindings( event.state );
};

Благодаря этому можно перемещаться назад и вперед по истории сеанса ожидаемым образом. Теперь вы увидите смену докладчиков соответственно нажатию кнопок «назад» и «вперед» в браузере.

Что дальше?

Как это часто бывает, дьявол кроется в деталях. Я лишь поверхностно обрисовал History API и ничего не сказал о том, что делать, если пользователь помещает докладчика в избранное и позднее заходит на этот сайт или просто вызывает обновление страницы, когда находится на одной из этих новых страниц докладчиков, созданных мной.

Я специально не стал рассматривать этот сценарий, так как обрабатывать его можно самыми разными способами, но они зависят от того, как вы структурировали свой сайт и какие технологии вы используете. Если вы применяете # или #!, то, возможно, вам будет достаточно вызвать window.location.hash, чтобы получить фрагмент URL, а затем вызвать сервис для извлечения соответствующих данных и связать их со своей разметкой.

Вместо того чтобы полагаться в выборе действий на пользовательские агенты, вы должны использовать такие средства, как Modernizr, и запрашивать браузер, что он способен делать.

Важно отметить, что, хотя мое решение создает полностью динамическую страницу, History API можно использовать только для части существующей страницы, чтобы основная часть страницы использовала преимущества сервера, а часть — History API. Отличный пример этому вы найдете по ссылке bit.ly/vOlB2U.

Вы также должны реализовать распознавание возможностей в своем веб-приложении. Вместо того чтобы полагаться в выборе действий на пользовательские агенты, вы должны применять такие средства, как Modernizr (modernizr.com), и запрашивать браузер, что он способен делать. Если браузер пользователя не поддерживает конкретную функциональность, можно задействовать поли-заполнение (polyfill) — прослойку, реализующую эту функциональность за браузер. Это можно делать даже для такой функциональности, как CSS. Подробнее о распознавании возможностей см. в статье Брэндона Сэтрома (Brandon Satrom) «No Browser Left Behind: An HTML5 Adoption Strategy» за сентябрь 2011 г. (msdn.microsoft.com/magazine/hh394148).

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

В этой статье все было сделано в Windows 8 Release Preview с применением Microsoft Web Matrix. Весь исходный код вы найдете по ссылке on.csell.net/msdn-historylesson, а ряд отличных ресурсов для изучения History API — по ссылке on.csell.net/msdn-historylesson-linkstack.

Автор: Кларк Селл  •  Иcточник: MSDN Magazine  •  Опубликована: 04.12.2012
Нашли ошибку в тексте? Сообщите о ней автору: выделите мышкой и нажмите CTRL + ENTER
Теги:   HTML5.


Оценить статью:
Вверх
Комментарии посетителей RSS

Чтобы оставить комментарий, зарегистрируйтесь или войдите с учетной записью социальной сети.