Что такое оффлайновое веб-приложение? На первый взгляд это звучит как противоречие в терминах. Веб-страница это то, что вы загружаете и отображаете, загрузка предполагает подключение к сети. Как вы можете скачивать в автономном режиме? Конечно, не можете. Но вы можете скачать, когда вы находитесь в сети. Вот как работает оффлайновое приложение в HTML5.
В простейшем случае оффлайновое веб-приложение представляет собой список адресов — HTML, CSS, JavaScript, изображения или любые другие ресурсы. Главная страница оффлайнового приложения получает этот список, вызывая манифест — текстовый файл, хранящийся на веб-сервере. Браузер, работающий с приложением, читает список адресов из файла манифеста, скачивает ресурсы, кэширует их локально и автоматически сохраняет локальные копии до момента их изменения. Когда в следующий раз вы попытаетесь получить доступ к веб-приложению без подключения к сети, браузер автоматически переключится на локальную копию.
Отсюда начинается работа для вас, как веб-разработчика. В DOM есть флаг, который говорит, в онлайне вы или оффлайне. Есть события, который происходят при изменении оффлайнового статуса (сейчас вы в автономном режиме, в следующую минуту вы уже в Интернете и наоборот). Но это еще не все. Если ваше приложение создает данные или сохраняет состояние, вы можете хранить информацию локально, пока вы отключены от сети, и синхронизировать ее с удаленным сервером, как только возвращаетесь в онлайн. Другими словами, HTML5 позволяет перенести ваше приложение в автономный режим.
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
Оффлайновое веб-приложение вращается вокруг файла манифеста кэша. Что это за файл манифеста? Это список всех ресурсов требуемых вашему веб-приложению, пока оно отключено от сети. Для загрузки и кэширования этих ресурсов вы должны указать файл манифеста с помощью атрибута manifest у элемента <html>.
<!DOCTYPE HTML> <html manifest="/cache.manifest"> <body> ... </body> </html>
Ваш файл манифеста кэша может располагаться в любом месте веб-сервера, но вы должны обеспечить для него тип text/cache-manifest. Если вы запускаете веб-сервер под Apache, то можете добавить директиву AddType в файл .htaccess в корне сайта.
AddType text/cache-manifest .manifest
Убедитесь, что имя вашего файла манифеста заканчивается на .manifest. Если вы используете другой веб-сервер или конфигурацию Apache, посмотрите документацию по управлению заголовком Content-Type.
☞В. Мое веб-приложение содержит более одной страницы. Нужно ли указывать атрибут manifest
на каждой странице или добавить его только на главную?
О. На каждой странице вашего приложения нужен атрибут manifest
, который указывает на манифест кэша всего веб-приложения.
Итак, каждая из ваших HTML-страниц указывает на файл манифеста кэша, сам файл передается с правильным заголовком Content-Type. Но что происходит в файле манифеста? Здесь все намного интереснее.
Первая строка каждого файла манифеста заключается в следующем.
CACHE MANIFEST
После чего все файлы манифеста можно разделить на три раздела: «явный», «резервный» и «онлайновый белый список». Каждый раздел имеет заголовок на отдельной строке. Если файл манифеста не имеет никаких заголовков раздела, все перечисленные ресурсы подразумеваются в разделе «явный». Постарайтесь не зацикливаться на терминологии, иначе голова взорвется.
Вот корректный файл манифеста. В нем перечислены три ресурса: CSS-файл, файл JavaScript и изображение в формате JPEG.
CACHE MANIFEST /clock.css /clock.js /clock-face.jpg
Этот файл манифеста кэша не имеет заголовков, так что все перечисленные ресурсы по умолчанию находятся в разделе «явный». Ресурсы в этом разделе скачиваются и кэшируются локально и будут использоваться вместо онлайновых копий при отключении от сети. Таким образом, при загрузке этого файла манифеста, ваш браузер скачает clock.css, clock.js и clock-face.jpg в корневой директории веб-сервера. Теперь вы можете отсоединить сетевой кабель и обновить страницу, все эти ресурсы будут доступны в оффлайне.
☞В. Нужно ли мне перечислять мои HTML-страницы в манифесте кэша?
О. Да и нет. Если ваше веб-приложение целиком содержится в одной странице, просто убедитесь, что страница указывает на манифест кэша с помощью атрибута manifest
. Когда вы переходите на HTML- страницу с атрибутом manifest
, сама страница считается частью веб-приложения, так что вам не нужно указывать ее в файле манифеста. Однако если ваше веб-приложение содержит несколько страниц, вы должны перечислить все HTML-страницы в файле манифеста, в противном случае браузер не будет знать, что есть другие HTML-страницы, которые должны быть загружены и кэшированы.
Вот более сложный пример. Предположим, вы хотите, чтобы ваше приложение отслеживало посетителей, используя скрипт tracking.cgi, который динамически загружается из <img src>. Кэширование этого ресурса провалит отслеживание, поэтому его нельзя кэшировать и оно никогда не должно быть доступно в автономном режиме. Вот что надо сделать.
CACHE MANIFEST NETWORK: /tracking.cgi CACHE: /clock.css /clock.js /clock-face.jp
Этот файл манифеста включает разделы заголовков. Строка, отмеченная как NETWORK: это начало раздела «онлайновый белый список». Ресурсы в этом разделе никогда не кэшируются и не доступны в автономном режиме (попытка загрузить их в оффлайновом режиме приведет к ошибке). Строка, отмеченная как CACHE: это начало раздела «явный». Остальное в этом файла такое же, как в предыдущем примере. Каждый из трех перечисленных ресурсов будет храниться в кэше, и доступен в режиме оффлайн.
Существует еще один тип раздела в файле манифеста: резервный раздел. В этом разделе вы можете определить замену для интернет-ресурсов, которые по какой-либо причине не могут быть сохранены в кэше или не кэшируются успешно. Спецификация HTML5 предлагает такой хитрый пример использования резервного раздел.
CACHE MANIFEST FALLBACK: / /offline.html NETWORK: *
Что здесь происходит? Вначале рассмотрим сайт, который содержит миллионы страниц, вроде Википедии. Вы не можете загрузить сайт целиком, как бы этого не хотелось. Предположим, что вы могли бы сделать его частично доступным в оффлайне. Но как вы решите, какие страницы в кэше? Примерно так: каждая просмотренная страница гипотетически доступной в оффлайне Википедии будет закачана и кэширована. Кэш будет включать каждую запись энциклопедии, которую вы когда-либо посещали, каждую страницу обсуждения и каждую страницу для правок (на которой вы можете вносить изменения в запись).
Вот что этот манифест кэша делает. Пусть каждая HTML-страница (запись, страница обсуждения, страница правок, страница истории) в Википедии указывают на этот файл манифеста. При посещении любой страницы, которая указывает на манифест кэша, ваш браузер говорит: «Эй, эта страница является частью оффлайнового приложения, и что я о ней знаю?». Если ваш браузер еще ни разу не скачивал этот файл манифеста, будет создан новый оффлайновый кэш приложения (appcache), скачаны все ресурсы, перечисленные в манифесте кэша, а затем текущая страница добавлена в appcache. Если ваш браузер знает об этом манифесте кэша, он просто добавит текущую страницу в существующий кэш приложения. Так или иначе, страница, которую вы только что посетили, оказывается в кэше приложения. Это важно и означает, что вы можете иметь оффлайновое веб-приложение, которое «лениво» добавляет страницы при их посещении. Вам не нужно перечислять каждую из ваших HTML-страниц в манифесте кэша.
Теперь посмотрим на резервный раздел FALLBACK: , в этом манифесте он появляется только в одной строке. Первая часть строки (до пробела) не URL, а шаблон URL. Первый символ (/) соответствует любой странице вашего сайта, не только главной. При попытке посетить страницу, пока вы находитесь в оффлайновом режиме, браузер будет искать ее в кэше приложения. Если ваш браузер находит в нем страницы (потому что вы ее посещали, пока были подключены к сети и страница в этот момент неявно добавляется в appcache), то ваш браузер будет отображать кэшированную копию страницы. Если Ваш браузер не обнаружил страницу в кэше приложения, вместо отображения сообщения об ошибке, появится страница /offline.html, указанная во второй половине строки резервного раздела.
Наконец, давайте рассмотрим сетевой раздел NETWORK:. Этот раздел в манифесте занимает одну строку, которая содержит один символ (*). Этот символ имеет особое значение в сетевом разделе, он называется «подстановочный флаг онлайнового белого списка». Это причудливый способ сказать: все, что не в appcache, должно быть загружено по исходному веб-адресу при подключении к Интернету. Это важно для «открытых» оффлайновых веб-приложений и означает, что пока вы просматриваете эту гипотетическую автономную Википедию по сети, ваш браузер будет получать изображения, видео и другие встроенные ресурсы как обычно, даже если они находятся на другом домене. Это характерно для больших сайтов, даже если они не являются частью оффлайнового веб-приложений. HTML-страницы генерируются локально, в то время как изображения и видео загружаются с другого домена.
Без подстановочного флага наша гипотетическая оффлайновая Википедия будет вести себя странно, когда вы в онлайне, в частности, она не будет загружать изображения или видео извне.
Является ли это пример завершенным? Нет. Википедия это больше, чем HTML-файлы. В ней используются общие CSS, JavaScript и картинки на каждой странице. Все эти ресурсы должны быть указаны в явном виде в разделе CACHE: файла манифеста, чтобы страницы в оффлайне правильно отображались и обрабатывались. Но указание резервного раздела делает так, что вы имеете открытое оффлайновое приложение, которое выходит за рамки тех ресурсов, что мы перечислили в явном виде в файле манифеста.
Итак, я рассказал об оффлайновых веб-приложениях, манифесте кэша и автономном кэше приложения (appcache) в расплывчатых, полумагических терминах. Всякие штуки загружаются, браузеры принимают решения и все просто работает. Вы знаете что-то лучше этого? Я имею в виду, что мы говорим про веб-разработку. Ничего так просто не работает.
Вначале давайте поговорим о потоке событий. В частности, событиях DOM. Когда ваш браузер посещает страницу, которая указывает на манифест кэша, происходит серия событий в объекте window.applicationCache. Я знаю, что это кажется сложным, но поверьте мне, это простейший вариант, что я смог придумать и не потерять ничего важного.
Если в любой момент в этом процессе что-то пойдет не так, ваш браузер вызовет событие error и остановится. Вот очень сокращенный список вещей, которые могут привести к неприятностям.
Я хочу отметить здесь два важных момента. Первый вы только что прочитали, но держу пари, не осознали, поэтому еще раз: если хотя бы один ресурс, упомянутый в файле манифеста, не будет скачан правильно, весь процесс кэширования вашего оффлайнового приложения провалится. Ваш браузер будет вызывать событие error, но не сообщает, в чем состоит проблема. Это может сделать отладку оффлайнового веб-приложения ужаснее, чем обычно.
Второй важный момент не связан, технически говоря, с ошибкой, но будет выглядеть как серьезная ошибка браузера, пока вы не поймете, что происходит. Связано это с тем, как браузер проверяет, что файл манифеста кэша изменился. Это трехэтапный процесс, хотя он скучен, но важен, так что будьте внимательны.
Любой из этих шагов может сбить вас с толку, пока вы разрабатываете и тестируете ваше оффлайновое веб-приложение. Например, у вас используется одна версия файла манифеста кэша, через 10 минут вы понимаете, что нужно добавить еще один ресурс. Не проблема, верно? Просто добавьте еще одну строку. Бдыщь. Вот что случится: вы обновите страницу, браузер обнаружит атрибут manifest, сработает событие checking, а затем… ничего. Ваш браузер упорно настаивает на том, что файл манифеста не изменился. Почему? Потому что ваш веб-сервер, скорее всего, по умолчанию настроен сообщать браузеру кэшировать статичные файлы в течение нескольких часов (используюя HTTP-заголовок Cache-Control). Это означает, что браузер никогда не пройдет первый шаг этого трехэтапного процесса. Конечно, веб-сервер знает, что файл был изменен, но ваш браузер даже не догадается спросить об этом сервер. Почему? Потому что в последний раз ваш браузер скачал манифест кэша, веб-сервер сказал ему кэшировать ресурсы несколько часов (используюя HTTP-заголовок Cache-Control). И теперь, спустя 10 минут, браузер так и делает.
Для ясности, это не ошибка, это особенность. Все работает именно так, как и предполагалось. Если у веб-сервера нет способа сказать браузерам (и промежуточным прокси) про кэширование, веб рухнет в одночасье. Но это не успокоит после того, как вы потратите несколько часов, пытаясь выяснить, почему ваш браузер не заметил обновленный кэш манифест. Еще лучше, если вы ждали достаточно долго и все таинственным образом заработает снова! Потому что срок кэша истек! Так, как и должно быть! Убей меня! Убей меня сейчас!
Так вот одну вещь вы должны сделать точно: перенастроить ваш веб-сервер так, чтобы файл манифеста не кэшировался по HTTP. Если вы используете Apache в качестве веб-сервера, эти две строки в вашем файле .htaccess сделают свое дело.
ExpiresActive On ExpiresDefault "access"
Фактически это отключит кэширование для всех файлов в этом каталоге и всех подкаталогах. Вероятно это не то, что вы хотели бы в действительности, так что вы должны либо связать строки с директивой <Files>, чтобы влиять только на файл манифеста, либо создать подкаталог, который содержит только .htaccess и файл манифеста. Как обычно, сведения о конфигурации зависят от веб-сервера, поэтому обратитесь к его документации о том, как управлять HTTP-заголовками кэширования.
Как только вы отключите HTTP-кэширование для файла манифеста, у вас еще будет время изменить один из ресурсов в appcache, но по тому же адресу на сервере. И вот здесь шаг 2 из трехэтапного процесса покажет вам. Если файл манифеста не изменился, браузер никогда не заметит, что один из ранее кэшированных ресурсов изменился. Рассмотрим следующий пример.
CACHE MANIFEST # rev 42 clock.js clock.css
Если вы измените clock.css, то не увидите изменений, потому что файл манифеста не поменялся. Каждый раз, когда вы вносите модификации в один из ресурсов вашего оффлайнового веб-приложения, вам необходимо менять и сам файл манифеста. Это может быть всего лишь замена одного символа. Я обнаружил простой метод, это включить комментарий с номером ревизии. Меняем номер ревизии в комментарии и веб-сервер возвращает новый измененный файл манифеста. Браузер замечает, что содержимое файла изменилось и запустит процесс повторной закачки всех ресурсов перечисленных в манифесте.
CACHE MANIFEST # rev 43 clock.js clock.css
Помните игру Уголки, которая была представлена в главе про холст, а затем улучшена за счет сохранения состояния в локальном хранилище? Давайте перенесем Уголки в оффлайн.
Чтобы сделать это, нам нужно составить список всех ресурсов этой игры. Итак, есть главная HTML-страница, один файл JavaScript, содержащий весь код игры, и… все. Картинок нет, потому что все рисуется программно через API Canvas. Все необходимые стили находятся внутри <style> в верхней части страницы. Вот наш манифест кэша.
CACHE MANIFEST halma.html ../halma-localstorage.js
Несколько слов о путях. Внутри каталога examples я создал подкаталог offline и файл манифеста живет в нем. Из-за того, что HTML-страницам необходимо одно небольшое дополнение для работы в оффлайне (об этом чуть позже) я создал отдельную копию HTML-файла, который также живет внутри offline. Поскольку нет никаких изменений в коде JavaScript, я повторно использую тот же файл .js, который хранится в родительском каталоге (examples). Все файлы выглядят следующим образом.
/examples/localstorage-halma.html /examples/halma-localstorage.js /examples/offline/halma.manifest /examples/offline/halma.html
В файле манифеста (/examples/offline/halma.manifest) мы хотим указать два файла. Во-первых, оффлайновую версию HTML-файла (/examples/offline/halma.html). Поскольку эти файлы хранятся в одном каталоге, они указаны в файле манифеста без каких-либо префиксов пути. Во-вторых, файл JavaScript, который живет в родительском каталоге (/examples/halma-localstorage.js). Перечисление в файле манифеста включает относительный путь: ../halma-localstorage.js. Это похоже на использование относительных путей в атрибуте src тега <img>. Как вы увидите в следующем примере, вы также можете использовать абсолютные пути (которые начинаются с корня текущего домена) или даже абсолютный адреса (которые указывают на ресурсы других доменов).
Теперь в HTML-файл мы должны добавить атрибут manifest, который указывает на файл манифеста кэша.
<!DOCTYPE html> <html manifest="halma.manifest">
И это все! Когда браузер способный на работу оффлайн загрузит первый раз страницу с поддержкой оффлайн, он скачивает указанный файл манифеста и начинает загрузку всех упомянутых ресурсов, сохраняя их в автономном кэше приложения. Вы можете играть в игру в оффлайне и локально сохранять ее состояние.
Сайт