Книга: JavaScript. Подробное руководство, 6-е издание

20.4.3. Автономные веб-приложения

20.4.3. Автономные веб-приложения

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

Чтобы выполняться в автономном режиме, веб-приложение должно иметь возможность выяснить, работает ли оно в автономном режиме, и определять моменты подключения и отключения от сети. Проверить режим работы броузера можно с помощью свойства navigator.onLine. А определить изменение состояния подключения можно, зарегистрировав обработчики событий «online» и «offline» в объекте Window.

Эта глава завершается простым примером автономного веб-приложения, демонстрирующим использование этих приемов. Приложение называется PermaNote - это простое приложение управления заметками, которое сохраняет текст, введенный пользователем, в объекте localStorage и выгружает его на сервер, когда соединение с Интернетом будет доступно.[56]

Приложение PermaNote дает пользователю создать единственную заметку и игнорирует проблемы аутентификации и авторизации - предполагается, что сервер обладает некоторым механизмом, позволяющим ему отличать одного пользователя от другого без использования какой-либо страницы входа. Реализация приложения PermaNote состоит из трех файлов. В примере 20.5 приводится содержимое файла объявления. В нем перечислены остальные два файла и указывается, что URL «note» не должен кэшироваться: этот URL-адрес используется для чтения и записи заметки на сервере.

Пример 20.5. permanote.appcache

CACHE MANIFEST
# PermaNote v8
permanote.html
permanote.js
NETWORK:
note

В примере 20.6 приводится второй файл приложения PermaNote: это HTML-файл, который реализует пользовательский интерфейс простого редактора. Он отображает элемент <textarea> с панелью инструментов вдоль верхнего края и строкой состояния для сообщений вдоль нижнего края. Обратите внимание, что тег <html> имеет атрибут manifest.

Пример 20.6. permanote.html

<!DOCTYPE HTML>
<html manifest="permanote.appcache">
  <head>
    <title>PeflaKTop PermaNote</title>
    <script src=”permanote. js"x/script>
    <style>
      #editor { width: 100%: height: 250px; }
      #statusline { width: 100%: }
    </style>
  </head>
  <body>
    <div>
      <button>Сохранить</button>
      <button>Синхронизировать</button>
      <button>0бновить пpилoжeниe</button>
    </div>
    <textarea></textarea>
    <div></div>
  </body>
</html>

Наконец, в примере 20.7 приводится сценарий на языке JavaScript, который обеспечивает работу веб-приложения PermaNote. Он определяет функцию status() для отображения сообщений в строке состояния, функцию save() - для сохранения текущей версии заметки на сервере и функцию sync() - для синхронизации серверной и локальной копии заметки. Функции save() и sync() используют приемы управления протоколом HTTP, описанные в главе 18. (Интересно отметить, что функция save() использует HTTP-метод «PUT» вместо более типичного для таких случаев метода «POST».)

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

onload

Пытается загрузить заметку с сервера, если там хранится более новая ее версия, и по завершении синхронизации разрешает доступ к окну редактора. Функции save() и sync() выполняют HTTP-запросы и регистрируют обработчик события «onload» в объекте XMLHttpRequest, чтобы определить момент, когда выгрузка или загрузка будут завершены.

onbeforeunload

Сохраняет текущую версию заметки на сервере, если она еще не была выгружена.

oninput

Всякий раз, когда текст в элементе <textarea> изменяется, он сохраняется в объекте localStorage, и запускается таймер. Если пользователь не продолжит редактирование в течение 5 секунд, заметка будет выгружена на сервер.

onoffline

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

ononline

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

onupdateready

Если появилась новая версия приложения, выводится сообщение в строке состояния, сообщающее об этом пользователю.

onnoupdate

Если приложение не изменилось, сообщает пользователю, что он или она работает с текущей версией.

А теперь, после краткого обзора логики работы приложения PermaNote, в примере 20.7 приводится ее реализация.

Пример 20.7'. permanote.js

// Некоторые необходимые переменные
var editor, statusline, savebutton, idletimer;
// При первой загрузке приложения
window.onload = function() {
  // Инициализировать локальное хранилище, если это первый запуск
    if (localStorage.note == null) localStorage.note = "";
    if (localStorage.lastModified == null) localStorage.lastModified = 0;
    if (localStorage.lastSaved == null) localStorage.lastSaved = 0;
  // Отыскать элементы, которые составляют пользовательский интерфейс редактора.
  // Инициализировать глобальные переменные,
  editor = document.getElementById("editor");
  statusline = document.getElementById("statusline");
  savebutton = document.getElementById("savebutton");
  editor.value = localStorage.note; // Восстановить сохраненную заметку
  editor.disabled = true; // Но запретить редактирование до синхр.
  // При вводе нового текста в элемент textarea
  editor.addEventListener("input",
                          function (e) {
                            // Сохранить новую заметку в localStorage
                            localStorage.note = editor.value;
                            localStorage.lastModified = Date.now();
                            // Переустановить таймер ожидания
                            if (idletimer) clearTimeout(idletimer);
                              idletimer = setTimeout(save, 5000);
                            // Разрешить кнопку сохранения
                            savebutton.disabled = false;
                          },
                          false);
  // При каждой загрузке приложения пытаться синхронизироваться с сервером
  sync();
};
// Сохраняет заметку на сервере перед уходом со страницы
window.onbeforeunload = function() {
  if (localStorage.lastModified > localStorage.lastSaved) save();
};
// Сообщить пользователю перед переходом в автономный режим
window.onoffline = function() { status("Автономный режим"); }
// При подключении к сети выполнить синхронизацию,
window.ononline = function() { sync(); };
// Сообщить пользователю, если доступна новая версия приложения.
// Здесь можно было бы выполнить перезагрузку принудительно, вызвав
// метод location.reload()
window.applicationCache.onupdateready = function() {
  status("Доступна новая версия приложения. " +
         "Чтобы использовать ее, необходимо перезагрузить приложение ”);
};
// Также сообщить пользователю, если он использует последнюю версию приложения,
window.applicationCache.onnoupdate = function() {
  status("Bы используете последнюю версию приложения.");
};
// Функция отображения сообщения в строке состояния
function status(msg) { statusline.innerHTML = msg; }
// Выгружает текст заметки на сервер (если сеть подключена).
// Автоматически вызывается через 5 секунд простоя после изменения текста заметки,
function save() {
  if (idletimer) clearTimeout(idletimer);
  idletimer = null;
  if (navigator.onLine) {
    var xhr = new XMLHttpRequest();
    xhr.open("PUT", "/note");
    xhr.send(editor.value);
    xhr.onload = function() {
      localStorage.lastSaved = Date.now();
      savebutton.disabled = true;
    };
  }
}
// Проверяет наличие новой версии заметки на сервере. Если она отсутствует,
// сохраняет текущую версию на сервере.
function sync() {
  if (navigator.onLine) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "/note");
    xhr.send();
    xhr.onload = function() {
      var remoteModTime = 0;
      if (xhr.status == 200) {
        var remoteModTime = xhr.getResponseHeader("Last-Modified");
        remoteModTime = new Date(remoteModTime).getTime();
      }
      if (remoteModTime > localStorage.lastModified) {
        status("Ha сервере найдена более новая заметка.");
        var useit =
          confirm("Ha сервере имеется более новая версияn” +
                  "заметки. Щелкните на кнопке Ok, чтобыn" +
                  "использовать эту версию, или на кнопкеn"+
                  "Отмена, чтобы продолжить редактироватьn"+
                  "текущую версию и затереть версию на сервере ");
        var now = Date.now();
        if (useit) {
          editor.value = localStorage.note = xhr.responseText;
          localStorage.lastSaved = now;
          status("Загружена более новая версия.");
        }
        else
          status("Игнорируется более новая версия заметки.");
        localStorage.lastModified = now;
      }
      else
        status("Редактируется последняя версия заметки.");
      if (localStorage.lastModified > localStorage.lastSaved) {
        save();
      }
      editor.disabled = false; // Разрешить доступ к редактору
      editor.focus();          // И поместить в него курсор ввода
    }
  }
  else { // В автономном режиме мы не можем синхронизироваться
    status("Невозможно синхронизироваться в автономном режиме");
    editor.disabled = false; editor.focus();
  }
}

Оглавление книги


Генерация: 0.093. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз