Руководство по использованию кэша приложения

Эрик Бидельман

Оригинал: http://www.html5rocks.com/tutorials/appcache/beginner/

Перевод: Влад Мержевич

Доступность в оффлайне становится всё более важной для веб-приложений. Да, все браузеры имеют механизмы кэширования, но они ненадежны и работают не всегда ожидаемо. HTML5 устраняет некоторые из этих неприятностей с помощью интерфейса ApplicationCache.

Использование интерфейса кэша даёт вашему приложению три преимущества:

  • автономный просмотр — пользователи могут исследовать ваш сайт целиком, когда они находятся в оффлайне;
  • скорость — ресурсы кэшируются локально, поэтому загружаются быстрее;
  • снижение нагрузки на сервер — браузер загружает с сервера только изменённые ресурсы.

Кэш приложения (или AppCache) позволяет разработчику указать, какие файлы браузер должен кэшировать и сделать доступными для оффлайновых пользователей. Ваше приложение будет работать корректно, даже если пользователь нажимает кнопку «Обновить», находясь в автономном режиме.

Файл манифеста кэша

Файл манифеста кэша является простым текстовым файлом со списком ресурсов, которые браузер должен кэшировать для доступа в автономном режиме.

Справка по файлу манифеста

Чтобы разрешить кэш приложения включите атрибут manifest в тег <html>.

<html manifest="example.appcache">
 ...
</html>

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

Атрибут manifest задаёт абсолютный или относительный URL, но абсолютный путь должен быть в рамках того же веб-приложения. Файл манифеста может иметь любое расширение, но требуется указать правильный MIME-тип (см.ниже).

<html manifest="http://www.example.com/example.mf">
 ...
</html>

Файл манифеста должен отдаваться с MIME-типом text/cache-manifest. Возможно, вам придётся добавить пользовательский тип файла на веб-сервере или через настройку .htaccess. Например, чтобы настроить этот MIME-тип в Apache, добавьте в конфигурационный файл:

AddType text/cache-manifest .appcache

Или в файл app.yaml на Google App Engine:

- url: /mystaticdir/(.*\.appcache)
  static_files: mystaticdir/\1
  mime_type: text/cache-manifest
  upload: mystaticdir/(.*\.appcache)

Структура файла манифеста

Простой манифест может выглядеть примерно так:

CACHE MANIFEST
index.html
stylesheet.css
images/logo.png
scripts/main.js

В этом примере кэшируется четыре файла указанных в манифесте.

Следует отметить несколько вещей:

  • строка CACHE MANIFEST должна идти первой, и обязательна;
  • данные кэширования сайтов ограничены 5 Мб. Однако если вы пишете приложение для Chrome Web Store, использование unlimitedStorage снимает это ограничение;
  • если файл манифеста или ресурс, указанный в нём не может быть скачан, весь процесс обновления кэша провалится, браузер станет использовать старый кэш приложения.

Давайте рассмотрим более сложный пример:

CACHE MANIFEST
# 2010-06-18:v2

# Явно кэшируемые основные записи  CACHE:
/favicon.ico  
index.html  
stylesheet.css  
images/logo.png  
scripts/main.js    

# Ресурсы, которые потребуются пользователю в онлайне  
NETWORK:  
login.php  
/myapi  
http://api.twitter.com    

# static.html будет использоваться, если main.py недоступен  
# offline.jpg будет использоваться вместо всех изображений в images/large/  
# offline.html будет использоваться вместо всех HTML-файлов   
FALLBACK:  
/main.py /static.html  
images/large/ images/offline.jpg  
*.html /offline.html

Строки, начинающиеся с решётки (#), являются комментариями, но также могут служить и другой цели. Кэш приложения обновляется только при изменении файла манифеста. Так, например, при редактировании изображений или функций JavaScript, эти изменения не будут кэшированы повторно. Вы должны изменить файл манифеста, чтобы сообщить браузеру обновить файлы в кэше. Создание комментария с номером версии, контрольной суммой или датой это один из способов гарантировать пользователям, что они используют последнюю версию. Вы можете также программно обновлять кэш, когда новая версия будет готова, как описано в разделе про обновление кэша.

Манифест может иметь три различных раздела: CACHE, NETWORK и FALLBACK.

CACHE: Это стандартный раздел для записи. Файлы, перечисленные в этом блоке (или сразу после  CACHE MANIFEST) будут явно кэшированы после того как они скачаны в первый раз.

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

FALLBACK: Дополнительный раздел указывает резервные страницы, если ресурс недоступен. Первый URL является ресурсом, второй резервом. Оба адреса должны быть относительны и быть в том же месте, что и файл манифеста. Можно использовать * для задания шаблона.

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

Следующий манифест определяет «универсальные» страницы (offline.html), которые будут отображаться, когда пользователь пытается получить доступ к корню сайта в автономном режиме. Он также заявляет, что все другие ресурсы (например, на удаленном сайте) требуют подключения к Интернету.

CACHE MANIFEST  
# 2010-06-18:v3    

# Явно кэшируемые записи  
index.html  
css/style.css    

# offline.html будет показан в автономном режиме   
FALLBACK:  
/ /offline.html    

# Все другие ресурсы (включая сайты) требуют подключение к сети   
NETWORK: 
*

# Дополнительные ресурсы в кэше  
CACHE:  
images/logo1.png  
images/logo2.png  
images/logo3.png

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

Обновление кэша

Оффлайновое приложение остаётся в кэше, пока не произойдёт одно из следующих событий:

  • пользователь очищает хранилище данных в браузере для вашего сайта;
  • файл манифеста изменился. Обновление файла упомянутого в манифесте не означает, что браузер будет повторно кэшировать этот ресурс. Файл манифеста сам должен быть заменён;
  • кэш приложения обновился программно.

Статус кэша

Объект window.applicationCache выступает вашим программным доступом к кэшу браузера. Свойство status используется для проверки текущего состояния кэша.

var appCache = window.applicationCache;    
switch (appCache.status) {   
  case appCache.UNCACHED: // UNCACHED == 0    
   return 'UNCACHED';    
   break;   
  case appCache.IDLE: // IDLE == 1    
   return 'IDLE';    
   break;   
  case appCache.CHECKING: // CHECKING == 2    
   return 'CHECKING';   
   break;   
  case appCache.DOWNLOADING: // DOWNLOADING == 3   
   return 'DOWNLOADING';   
   break; 
  case appCache.UPDATEREADY: // UPDATEREADY == 5   
   return 'UPDATEREADY';   
   break;   
  case appCache.OBSOLETE: // OBSOLETE == 5    
   return 'OBSOLETE';   
   break;  
  default:   
   return 'UKNOWN CACHE STATUS';   
   break;  
};

Для программного обновления кэша вначале вызывается applicationCache.update(). Он будет пытаться обновить кэш пользователя (который требует, чтобы файл манифеста изменился). Наконец, когда applicationCache.status находится в состоянии UPDATEREADY, вызов applicationCache.swapCache() обновит старый кэш на новый.

var appCache = window.applicationCache;    
appCache.update(); // Попытка обновить кэш пользователя    
  ...    
if (appCache.status == window.applicationCache.UPDATEREADY) {
   appCache.swapCache(); // Успешно, меняем на новый кэш  
}

Использование update() и swapCache() не служит обновлением ресурсов пользователю. Этот поток просто говорит браузеру проверить новый манифест, скачать указанное обновленное содержания и повторно заполнить кэш приложения. Таким образом, страница скачивается с сервера дважды, в первый раз заполняется новый кэш приложения, а во второй раз обновляется содержание страницы. Хорошая новость: вы можете избежать этого двойного скачивания. Для обновления у пользователя новой версии вашего сайта установите отслеживание события updateready во время скачивания страницы.

// Проверить, если новый кэш доступен при скачивании страницы  
window.addEventListener('load', function(e) {     
 window.applicationCache.addEventListener('updateready', function(e) {    
  if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {    
   // Браузер скачивает новый кэш      
   // Заменяет его и обновляет страницу      
   window.applicationCache.swapCache();     
   if (confirm('Новая версия этого сайта доступна. Загрузить её?')) {
     window.location.reload();     
   }
  } else {
     // Манифест не поменялся, ничего нового на сервере   
  }
 }, false);  
}, false);

События AppCache

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

function handleCacheEvent(e) {   
  //...  
}    
function handleCacheError(e) {   
  alert(Ошибка: Не удалось обновить кэш!');  
};    

// Выполняется после первого кэширования манифеста  
appCache.addEventListener('cached', handleCacheEvent, false);   

// Проверка обновления. Всегда идёт первым в последовательности событий  
appCache.addEventListener('checking', handleCacheEvent, false);    

// Обновление найдено, браузер забирает ресурсы  
appCache.addEventListener('downloading', handleCacheEvent, false);    

// Манифест вернул ошибку 404 или 410, загрузка прервана  
// или манифест изменился, пока происходил процесс загрузки  
appCache.addEventListener('error', handleCacheError, false);   

// Выполняется после первого скачивания манифеста  
appCache.addEventListener('noupdate', handleCacheEvent, false);    

// Выполняется, если файл манифеста возвращает 404 или 410.  
// Эти результаты в кэше приложения будут удалены   
appCache.addEventListener('obsolete', handleCacheEvent, false);    

// Выполняется для каждого ресурса перечисленного в манифесте, пока они забираются
appCache.addEventListener('progress', handleCacheEvent, false);    

// Выполняется, когда ресурсы манифеста были недавно скачаны  
appCache.addEventListener('updateready', handleCacheEvent, false);

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

Не выкладывайте свой код напрямую в комментариях, он отображается некорректно. Воспользуйтесь сервисом cssdeck.com или jsfiddle.net, сохраните код и в комментариях дайте на него ссылку. Так и результат сразу увидят.