Практические методы кэширования

163

Итак, мы узнали, как упаковать группу страниц и ресурсов в виде автономного приложения. Мы научились создавать файл манифеста, обновлять его и обеспечивать исполнение созданного автономного приложения в браузерах. Хотя этих знаний достаточно для создания простых автономных приложений, более сложные веб-сайты иногда требуют дополнительной работы. Например, мы можем хранить определенное содержимое онлайн, заменять определенные страницы в автономном режиме или определять (программным способом), подключен ли компьютер к интернету. Далее мы научимся реализовывать все эти задачи посредством более продвинутых манифестов и применением кода JavaScript.

Доступ к онлайновым файлам

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

Допустим, например, что на нашей автономной странице используются два изображения:

<img alt="logo" src="Images/logo.png">
<img alt="bear" src="Images/bear.jpg">

Но манифест кэширует только одно из этих изображений:

CACHE MANIFEST
Images/bear.jpg

Размышляя логически, можно предположить, что браузер возьмет изображение bear.jpg из своего кэша, а изображение logo.png запросит у веб-сервера (при условии, что компьютер подключен к интернету). Ведь так происходит при работе с обычными сайтами, когда браузер обращается с кэшированной страницы к странице, которая еще не была сохранена в кэше. Но в действительности в этом отношении автономные приложения работают по-другому. Браузер берет изображение bear.jpg из кэша, но игнорирует изображение logo.png, отображая вместо него либо специальный значок отсутствующего изображения, либо просто пустое место, в зависимости от браузера.

Эта проблема решается добавлением в манифест нового раздела. Он начинается с заголовка NETWORK:, а за ним следует список файлов, которые нужно получать с веб-сервера:

CACHE MANIFEST
Images/bear.jpg

NETWORK:
Images/logo.png

Теперь, когда компьютер подключен к интернету, браузер будет пытаться получить файл logo.png с веб-сервера, но при отсутствии подключения предпринимать такую попытку не будет.

Здесь у вас, наверное, возникает вопрос: зачем утруждать себя созданием явного списка файлов, которые мы не хотим кэшировать? Одна из причин — из-за возможностей хранилища, например, мы игнорируем файлы большого объема, чтобы наше приложение могло сохраниться в кэше браузеров, разрешающих максимум 5 Мбайт кэша для каждого автономного приложения.

Но более вероятной будет ситуация, когда у нас есть содержимое, которое должно предоставляться, когда запрашивается, но никогда не должно кэшироваться, например, отслеживающие сценарии или динамически создаваемая реклама. В таком случае, самым легким решением будет добавить звездочку (*) в раздел NETWORK. Это знак подстановки, указывающий браузеру обращаться к серверу за всеми ресурсами, которые не были явно кэшированы:

# ...
NETWORK:
*

Знак подстановки можно также использовать, чтобы указывать файлы конкретного типа (например, выражение *.jpg обозначает все изображения JPEG) или все файлы на определенном сервере (например, выражение http://www.google-analytics.com/* обозначает все ресурсы в домене Google Analytics).

У вас может возникнуть мысль, что манифест можно было бы упростить, используя знак подстановки для указания всех файлов, которые нужно сохранить в кэше, вместо того, чтобы задавать каждый отдельный файл. К сожалению (или к счастью), указание файлов для кэширования посредством звездочки не поддерживается, т.к. создатели HTML5 беспокоились, что небрежные веб-разработчики могут пытаться поместить в кэш огромные веб-сайты.

Добавление резервных решений

Манифест указывает браузерам, какие файлы нужно кэшировать, а какие получать с сервера (перечисляются в разделе NETWORK). Манифест также может содержать раздел для указания резервных решений, т.е. замены одного файла другим, в зависимости от наличия или отсутствия подключения к интернету.

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

FALLBACK:
MyPage.html MyPage_offline.html

Браузер загружает резервный файл (в данном случае это файл MyPage_offline.html) и кэширует его. Но браузер использует этот файл только в том случае, если компьютер не подключен к интернету, а при наличии подключения браузер запрашивает первый файл (т.е. в данном случае MyPage.html).

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

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

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

FALLBACK:
/ offline.html

Теперь допустим, что браузер запрашивает страницу, находящуюся на том же веб-сайте, что и автономное приложение, но которая не была кэширована. Если компьютер подключен к интернету, браузер попытается обратиться к серверу и получить живую страницу. Но при отсутствии подключения, или если веб-сайт недоступен по какой-либо другой причине, или запрашиваемой страницы просто нет на сервере, браузер отображает кэшированную страницу offline.html:

Резервная страница, если запрошенная не существует

В данном примере страница zzz.html не существует. Интересно, что браузер не обновляет соответствующим образом информацию в строке адреса, вследствие чего посетитель не знает настоящее название отображаемой страницы с сообщением об ошибке доступа.

В только что рассмотренном примере для обозначения любой страницы применяется несколько произвольная нотация подстановочного символа в виде косой черты (/). Это может показаться несколько странным в виду того, что в разделе NETWORK для этой же цели применяется звездочка. В действительности некоторые браузеры (например, Firefox) разрешают использование звездочки в качестве подстановочного символа в разделе резервных файлов. Это означает, что для таких браузеров предыдущий пример можно переделать:

FALLBACK:
* offline.html

Кроме этого, можно указать резервные ресурсы, совпадающие со всеми файлами в указанной папке:

FALLBACK:
http://mysite.com/paint_app/* offline.html

Или же ресурсы, совпадающие с определенными типами файлов:

FALLBACK:
*.jpg missing_picture.html

К сожалению, другие браузеры не понимают этот логичный синтаксис, по крайней мере, пока еще не понимают.

Проверка подключения

Раздел указания резервных файлов можно использовать в удобном JavaScript-методе для определения, находится ли браузер в режиме онлайн или нет. Опытные разработчики JavaScript, наверное, помнят свойство navigator.online, предоставляющее слегка ненадежный способ проверки, находится ли браузер в настоящее время в онлайн. Но проблема с этим методом состоит в том, что свойство online в действительности отображает состояние параметра браузера "Работать автономно", а не собственно наличие или отсутствие подключения к интернету.

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

Эта проблема решается с помощью резервного решения, которое загружает разные версии одной и той же функции JavaScript в зависимости от того, находится ли приложение онлайн или оффлайн. Раздел FALLBACK: манифеста будет таким:

FALLBACK:
online.js offline.js

Первоначальная версия веб-страницы ссылается на JavaScript-файл online.js. Он содержит эту очень простую функцию:

function isSiteOnline() { 
    return true;
}

Но если файл online.js недоступен, браузер использует вместо него файл offline.js, который содержит ту же функцию, но с другим результатом:

function isSiteOnline() { 
    return false;
}

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

if (isSiteOnline()) {
    // Можно выполнять задачи, требующие подключения к интернету, такие 
    // как взаимодействие с серверам с помощью объекта XMLHttpRequest
}
else {
    // Приложение исполняется в автономном режиме. Может быть,
    // следует скрыть или программным образом изменить определенное
    // содержимое или отключить некоторые возможности
}

Информирование об обновлениях с помощью JavaScript

С автономным приложением можно взаимодействовать посредством сравнительно ограниченного интерфейса на основе JavaScript-объекта applicationCache.

Свойство status объекта applicationCache указывает, какую операцию браузер выполняет в настоящее время — проверяет наличие обновленного манифеста, загружает новые файлы и т.п. Значение этого свойства часто обновляется, и оно почти такое же полезное, как и взаимодействующие события, которые срабатывают при изменении статуса объекта applicationCache:

События кэширования
Событие Описание
onChecking Когда браузер обнаруживает в веб-странице атрибут manifest, он активирует это событие и проверяет наличие соответствующего файла манифеста на веб-сервере
onNoUpdate Если браузер уже загрузил файл манифеста и этот файл не был изменен, он активирует это событие и не предпринимает никаких дальнейших действий
onDownloading Браузер активирует это событие, прежде чем начинать загрузку манифеста (и указанных в манифесте файлов). Это происходит при первой загрузке файлов манифеста и при обновлениях
onProgress Браузер периодически активирует это событие при загрузке файлов, чтобы информировать о ходе загрузки
onCached Событие указывает на завершение первоначальной загрузки для нового автономного приложения. После этого больше не происходит никаких событий
onUpdateReady Событие указывает на завершение загрузки обновленного содержимого. На этом этапе новое содержимое готово к использованию, но не отображается в окне браузера до тех пор, пока страница не будет перезагружена. После этого больше не происходит никаких событий
onError Где-то произошла ошибка. Возможно, недоступен веб-сервер (в таком случае страница переключается в автономный режим), возможно, в манифесте использован неправильный синтаксис или недоступен кэшированный ресурс. После этого события не происходит больше никаких других событий
onObsolete В процессе проверки на наличие обновлений браузер обнаружил отсутствие манифеста, после чего он очищает кэш. При следующей загрузке страницы браузер загрузит ее с веб-сервера

Объект applicationCache также предоставляет два метода для более специализированных сценариев. Первый метод, update(), проверяет наличие нового манифеста. В случае его существования метод начинает загрузку в фоновом режиме. Иначе не предпринимается никаких дальнейших действий.

Хотя браузеры проверяют наличие обновлений автоматически, если есть основания полагать, что манифест изменился после первой загрузки страницы, метод update() можно вызвать вручную. Этот метод может быть полезным в приложениях с продолжительным временем работы, например, когда пользователь работает на одной и той же странице в течение всего дня.

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

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

Пройди тесты
Лучший чат для C# программистов