Web 应用程序有离线功能,如保存大量数据集和二进制文件。你甚至可以做缓存 MP3 文件这样的事情。浏览器技术可以保存离线数据和大量的储存。但问题是,如何选择合适技术,如何方便灵活的实现。
如果你需要开发一个支持离线存储的 Web 应用程序,不知道从哪里开始,那么这篇文章正是你需要的。
localStorage 能够让你实现基本的数据存储,但它的速度慢,而且不能处理二进制数据。IndexedDB 和 WebSQL 是异步的,速度快,支持大数据集,但他们的API 使用起来有点复杂。不仅如此,IndexedDB 和 WebSQL 没有被所有的主流的浏览器厂商支持,这种情况最近也不太可能改变。
Mozilla 开发了一个叫 localForage 的库 ,使得离线数据存储在任何浏览器都是一项容易的任务。
localForage 是一个使用非常简单的 JavaScript 库的,提供了 get,set,remove,clear 和 length 等等 API,还具有以下特点:
对 IndexedDB 和 WebSQL 的支持使您可以为您的 Web 应用程序存储更多的数据,要比 localStorage 允许存储的多很多。其 API 的无阻塞性质使得您的应用程序更快,不会因为 Get/Set 调用而挂起主线程。
传统的 API 在许多方面其实是很不错的,使用简单,没有复杂的数据结构。如果你在你的应用程序有一个配置信息需要保持,可以这样写:
1
2
3
4
5
6
7
8
9
10
11
|
// 需要离线保存的配置数据
var
config = {
fullName: document.getElementById(
'name'
).getAttribute(
'value'
),
userId: document.getElementById(
'id'
).getAttribute(
'value'
)
};
// 保存起来,供下次使用
localStorage.setItem(
'config'
, JSON.stringify(config));
// 从离线存储中读取出来
var
config = JSON.parse(localStorage.getItem(
'config'
));
|
请注意,使用 localStorage 存储的数据需要保存为字符串,所以我们在保存和读取时需要进行 JSON 序列化和反序列化。
看起来好像使用很简单,但你很快会发现 localStorage 的几个问题:
它是同步的。不管数据多大,我们需要等待数据从磁盘读取和解析,这会减慢我们的应用程序的响应速度。这在移动设备上是特别糟糕的,主线程被挂起,直到数据被取出,会使你的应用程序看起来慢,甚至没有反应。
它仅支持字符串。需要使用 JSON.parse 与 JSON.stringify 进行序列号和反序列化。这是因为 localStorage 中仅支持 JavaScript 字符串值。不支持数值,布尔值,Blob 类型的数据。
localForage 可以解决上面的问题,下面我们对比一下 IndexedDB 和 localForage 存储相同数据的差异:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
// IndexedDB.
var
db;
var
dbName =
"dataspace"
;
var
users = [ {id: 1, fullName:
'Matt'
}, {id: 2, fullName:
'Bob'
} ];
var
request = indexedDB.open(dbName, 2);
request.onerror =
function
(event) {
// 错误处理
};
request.onupgradeneeded =
function
(event) {
db = event.target.result;
var
objectStore = db.createObjectStore(
"users"
, { keyPath:
"id"
});
objectStore.createIndex(
"fullName"
,
"fullName"
, { unique:
false
});
objectStore.transaction.oncomplete =
function
(event) {
var
userObjectStore = db.transaction(
"users"
,
"readwrite"
).objectStore(
"users"
);
}
};
var
transaction = db.transaction([
"users"
],
"readwrite"
);
// 所有数据都添加到数据后调用
transaction.oncomplete =
function
(event) {
console.log(
"All done!"
);
};
transaction.onerror =
function
(event) {
// 错误处理
};
var
objectStore = transaction.objectStore(
"users"
);
for
(
var
i
in
users) {
var
request = objectStore.add(users[i]);
request.onsuccess =
function
(event) {
// 里面包含我们需要的用户信息
console.log(event.target.result);
};
}
|
使用 WebSQL 实现可能不会那么太冗长,但也是有点复杂。使用 localForage,可以这样写:
1
2
3
4
5
|
// 保存用户信息
var
users = [ {id: 1, fullName:
'Matt'
}, {id: 2, fullName:
'Bob'
} ];
localForage.setItem(
'users'
, users,
function
(result) {
console.log(result);
});
|
是不是简单了很多?
比方说,你要下载一个用户的个人资料图片,并对其进行缓存以供离线使用。使用 localForage 很容易保存二进制数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 使用 AJAX 下载图片
var
request =
new
XMLHttpRequest();
// 以获取第一个用户的资料图片为例
request.open(
'GET'
,
"/users/1/profile_picture.jpg"
,
true
);
request.responseType =
'arraybuffer'
;
// 当 AJAX 调用完成,把图片保存到本地
request.addEventListener(
'readystatechange'
,
function
() {
if
(request.readyState === 4) {
// readyState DONE
// 保存的是二进制数据,如果用 localStorage 就无法实现
localForage.setItem(
'user_1_photo'
, request.response,
function
() {
// 图片已保存,想怎么用都可以
});
}
});
request.send()
|
下次,只用三行代码就可以从缓存中把照片读取出来:
1
2
3
4
|
localForage.getItem(
'user_1_photo'
,
function
(photo) {
// 获取到图片数据后,可以通过创建 data URI 或者其它方法来显示
console.log(photo);
});
|
如果你不喜欢在你的代码中使用回调,你可以使用 ES6 Promises,来替换 localForage 的回调参数。让我们使用上面的照片例子,看下使用 Promises 的代码:
1
2
3
4
|
localForage.getItem(
'user_1_photo'
).then(
function
(photo) {
// 获取到图片数据后,可以通过创建 data URI 或者其它方法来显示
console.log(photo);
});
|
localForage 支持所有现代浏览器(包括 IE8 及更高版本)。支持的浏览器和平台如下:
立即下载