离线Web应用离线Web应用指的是在浏览器处于离线的状态下依然能正常打开已经进行应用程序缓存的程序。应用点如时钟、可离线游戏等,可以在直接在本地运行。可使用localStorage来存储应用数据,在有网络的情况下再将游戏的最高分上传至服务器,实现多设备共享数据。有一个典型的案例,就是stackedit在线markdown编辑器(文章介绍),程序将文本使用localStorage存储起来,离线状态下照常可编辑;当在线的时候,可以将文本同步至如Google Drive,实现多设备共享,非常优秀的作品。是否在线为了在离线状态下可用,需要知道Web应用当前是离线还是在线状态,同时当网络连接的状态发生改变时也能感知到。通过navigator.online属性,可以检测浏览器是否在线。同时,在windows对象上注册在线和离线事件的处理程序,可以检测网络连接状态的改变。例子解析通过一个简单的记事本程序——PermaNote,来解释如何使用。程序将用户的文本保存到localStorage中,并且在网络连接可用的时候,将其上传到服务器,PermaNote只允许用户编辑单个笔记。PermaNote应用包含3个文件,一个应用清单文件、一个html页面文件,一个实现逻辑的js文件。Demo:http://xuanfengge.com/demo/201506/appcache/permanote.html1. permanote.appcacheCACHE MANIFEST
# PermaNote v8
permanote.html
permanote.js
NETWORK:
note2. permanote.html
PermaNote Editor
3. permanote.jsstatus()函数用于显示状态栏消息,save()函数将笔记本保存到服务器,sync()用于确保本地与服务器文本的同步。应用程序的时间处理程序解释:onload尝试和服务器同步,一旦有新版本的笔记并且完成同步后,就启用编辑器窗口。save()和sync()函数发出HTTP请求,并在XMLHttpRequest对象上注册一个onload时间处理程序来获取上传或者下载完成的提醒。onbeforeunload在未上传前,把当前版本的笔记数据保存到服务器上。oninput每当textarea输入框内容发生变化时,都将其内容保存到localStorage中,并启动一个计时器。当用户停止编辑超过5秒,将自动把数据保存到服务器。onoffline当浏览器进入离线状态时,在状态栏显示离线消息。ononline当浏览器回到在线状态时,同步服务器,检查是否有新版本的数据,并且保存当前版本的数据。onupdateready如果新版本的应用已缓存,则在状态栏展示消息告知用户。onnoupdate如果应用程序缓存没有发生变化,则同时用户仍在运行当前版本。// 定义全局变量
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;
// 查找编辑器UI元素,并初始化全局变量
editor = document.getElementById("editor");
statusline = document.getElementById("statusline");
savebutton = document.getElementById("savebutton");
editor.value = localStorage.note; // 初始化编辑器,将保存的笔记数据填充到内容
editor.disabled = true; // 同步前禁止编辑
// 当输入框内容发生变化
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("Offline"); }
// 再次返回在线状态时,进行同步
window.ononline = function() { sync(); };
// 当有新版本应用的时候,提醒用户
// 也可使用location.reload()放大来强制刷新应用
window.applicationCache.onupdateready = function() {
status("A new version of this application is available. Reload to run it");
};
// 当没有新版本的时候也通知用户
window.applicationCache.onnoupdate = function() {
status("You are running the latest version of the application.");
};
// 状态栏显示相关信息提示
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("Newer note found on server.");
var useit =
confirm("There is a newer version of the note\n" +
"on the server. Click Ok to use that version\n"+
"or click Cancel to continue editing this\n"+
"version and overwrite the server");
var now = Date.now();
if (useit) {
editor.value = localStorage.note = xhr.responseText;
localStorage.lastSaved = now;
status("Newest version downloaded.");
}
else
status("Ignoring newer version of the note.");
localStorage.lastModified = now;
}
else
status("You are editing the current version of the note.");
if (localStorage.lastModified > localStorage.lastSaved) {
save();
}
editor.disabled = false; // 再次启用编辑器
editor.focus(); // 将光标定位到编辑器中
}
}
else { // 离线状态下,不能同步
status("Can't sync while offline");
editor.disabled = false;
editor.focus();
}
}