客户端存储技术
介绍浏览器的客户端存储技术,包括:Cookie、Storage、IndexedDB、Application Cache和已经过时但还是很常见的Web SQL,以及一些其他的还未普及的存储技术:Cache Storage、FileSystem API。
Cookie
特点:
- 具有大小限制,4~8k
- 绑定到域名,对同一个域名的请求会自动带上cookie请求头
- 能够给cookie设置有效时间
cookie使用key=value的形式记录用户信息,多个键值对之间使用;分隔
前端通过操纵document下的cookie属性直接操纵cookie1
2document.cookie
//"irstime=1492081681236; uuid_tt_dd=242117069142"
与cookie相关的请求头有两个,一个是Set-Cookie(用在响应中,用于设置cookie和附加信息。一个Set-Cookie可以设置一对或多对键值对),一个是Cookie(用在请求时,表示发送的cookie)
Set-Cookie格式1
2
3Set-Cookie: <name>=<value>[; <name>=<value>]...
[; expires=<date>][; domain=<domain_name>]
[; path=<some_path>][; secure][; httponly]
Storage
Storage分为本地存储(Local Storage)和会话存储(Session Storage)。两者使 用完全相同的API,但本地存储会持久存在(或者直到用户清除),而会话存储只要浏览器 关闭就会消失。存储的键值只能为字符串,storage存储的键值也是和域名相挂钩的。
API
localStorge.setItem:设置特定键值;
localStorge.getItem:检索特定的键值;
localStorge.removeItem:删除键值及其相关联的值;
localStorge.clear:删除所有的键值(只限定于发出请求的特定域名)
IndexedDB
IndexedDB 数据库使用key-value键值对储存数据,IndexedDB 是事务模式的数据库(任何操作都发生在事务中),API 基本上是异步的(使用事件模型)。
IndexedDB是一个基于JavaScript的面向对象的数据库。
允许存储和检索用键索引的对象; 可以存储结构化克隆算法支持的任何对象;IndexedDB也遵循同源策略。
结构化克隆支持的对象:RegExp、Blob、File、Filelist、ImageData、CanvasPixelArray(克隆力度将会跟原始对象相同)、可以正确复制有循环引用的对象。
不支持的对象:
- Error、Function、DOM节点
- 对象的某些特定参数也不会被保留
- RegExp 对象的 lastIndex 字段不会被保留
- 属性描述符,setters 以及 getters(以及其他类似元数据的功能)同样不会被复制。例如,如果一个对象用属性描述符标记为 read-only,它将会被复制为 read-write,因为这是默认的情况下。
- 原形链上的属性也不会被追踪以及复制。
初始化数据库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
35
36
37
38
39
40
41
42
43var localDatabase = {};
localDatabase.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
localDatabase.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
var DB = "db_user",VERSION = 1.0;
/*打开数据库*/
function openDB(dbName, version, objStore, columnArr) {
var db = localDatabase.db;
try {
if (db) db.close();
version = version || 1;
// 打开数据库
var openRequest = localDatabase.indexedDB.open(dbName, version);
// 成功打开数据库
openRequest.onsuccess = function() {
localDatabase.db = openRequest.result;
};
// 创建或删除存储对象只能在此进行
openRequest.onupgradeneeded = function(e) {
var db = e.currentTarget.result;
if (!db.objectStoreNames.contains(objStore)) {
//创建存储对象并设置主键
var store = db.createObjectStore(objStore, {
keyPath: "id"
});
// 创建索引,方便根据字段进行搜索
for (var i = 0, len = columnArr.length; i < len; i++) {
store.createIndex(columnArr[i], columnArr[i], {
unique: false
});
}
console.log("创建存储对象成功")
}
};
openRequest.onerror = function(e){
console.error(e.message);
};
} catch (e) {
console.error(e.message);
}
}
插入和删除数据1
2
3
4
5
6
7
8
9
10
11/*添加数据*/
var request = localDatabase.db.transaction(objStore, "readwrite").objectStore(objStore).add(obj);
request.onsuccess = function(event) {
// 添加成功!
};
/*删除数据*/
var request = db.transaction(["customers"], "readwrite").objectStore("customers").delete("444-44-4444");
request.onsuccess = function(event) {
// 删除数据成功!
};
使用索引获取单个值,使用游标获取多个值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
35/*使用索引获取单个值*/
var transaction = db.transaction(objStore),
store = transaction.objectStore(objStore),
request = store.get(key);
request.onsuccess = function(evt) {
console.log(JSON.stringify(evt.target.result));
};
/* 使用游标获取多个值 */
var recordArr = [];
function queryAllRecords(objStore) {
recordArr = [];
var db = localDatabase.db;
try {
var transaction = db.transaction(objStore),
store = transaction.objectStore(objStore),
request = store.openCursor();
request.onsuccess = function(evt) {
var cursor = evt.target.result,
jsonStr = "";
if (cursor) {
var records = cursor.value;
jsonStr = jsonStr + JSON.stringify(records);
console.log(jsonStr);
recordArr.push(records);
cursor.continue();
}
};
} catch (e) {
console.error(e.message);
}
}
更多IDB代码见:IDB_Util
Application Cache
Application Cache使用的是一套缓存列表。所谓列表,只是一个非常简单的文本文档,其中列举了所有应该或不应该通过缓存机制处理的资源条目,从而指导浏览器下载特定文件、加以保存并在必要时予以使用——而不必再向服务器发出重复请求。
要使用Application Cache,我们需要首先在包含有缓存对象文件的网站中保存一个扩展名为.appcache的文本文件。根据所使用Web服务器的具体类型,我们可能需要为.appcache文件创建一个自定义MIME类型以确保它们能够正确作用于浏览器并可被作为应用程序缓存文件读取。
例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22CACHE MANIFEST
# 以上折行必需要写
CACHE:
# 这部分写需要缓存的资源文件列表
# 可以是相对路径也可以是绝对路径
index.html
index.css
images/logo.png
js/main.js
http://img.baidu.com/js/tangram-base-1.5.2.1.js
NETWORK:
# 可选
# 这一部分是要绕过缓存直接读取的文件
login.php
FALLBACK:
# 可选
# 这部分写当访问缓存失败后,备用访问的资源
# 每行两个文件,第一个是访问源,第二个是替换文件*.html /offline.html
html文件中的设置1
2<html manifest="demo.cache">
</html>
apache服务器中,定义tAddType text/cache-manifest .cache
缓存更新
更新manifest文件
浏览器发现manifest文件本身发生变化,便会根据新的manifest文件去获取新的资源进行缓存。
当manifest文件列表并没有变化的时候,我们通常通过修改manifest注释的方式来改变文件,从而实现更新。
使用js的方式1
2
3
4
5
6
7
8var appCache = window.applicationCache;
appCache.update(); //尝试更新缓存
if (appCache.status == window.applicationCache.UPDATEREADY) {
appCache.swapCache(); //更新成功后,切换到新的缓存
}
Web SQL
web sql的规范工作已经被W3C停止,按照官方的说法是web sql后台使用的都是SQLite,但为了实现规范前台会有许多不同的实现。因此规范被停止。但是仍然有许多老系统、插件使用web sql,因此学习web sql对我们还是有着不小的好处。
Web SQL Database 规范中定义了三个核心方法:
- openDatabase:这个方法使用现有数据库或新建数据库来创建数据库对象
- transaction:这个方法允许我们根据情况控制事务提交或回滚
- executeSql:这个方法用于执行SQL 查询
openDatabase1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var db = openDatabase(dbName, dbVersion, showName, size,callback);
db.transaction(function(context) {
context.executeSql('CREATE TABLE IF NOT EXISTS testTable (id unique, name)');
context.executeSql('INSERT INTO testTable (id, name) VALUES (0, "Byron")');
context.executeSql('INSERT INTO testTable (id, name) VALUES (1, "Casper")');
context.executeSql('INSERT INTO testTable (id, name) VALUES (2, "Frank")');
context.executeSql('SELECT * FROM testTable where id=? and name=?', [2, 'Frank'], function(context, results) {
var len = results.rows.length, i;
console.log('Got ' + len + ' rows.');
for (i = 0; i < len; i++) {
console.log('id: ' + results.rows.item(i).id);
console.log('name: ' + results.rows.item(i).name);
}
}, errorCallback);
}, successCallback, errorCallback);
其他
Cache Storage
Cache Storage用来存储 Response 对象的。也就是说用来对 HTTP ,响应做缓存的。CacheStorage 在浏览器上的引用名叫 caches ,它定义在 ServiceWorker 的规范中。CacheStorage 是多个 Cache 的集合,而每个 Cache 可以存储多个 Response 对象。CacheStorage的设计风格也和ServiceWork一样都基于Promise。
- match() 返回promise,resolve为response对象,如果在任意一个cache中找到与key对应的响应对象
- has() 返回promise对象,resolve为boolean,根据是否含有记录,得到不同的值
- open() 返回promise,resolve为cache,如果不存在对应的cache,则新建一个
- delete() 返回promise,resolve为boolean,如果删除cache成功则为true
- keys() 返回promise,resolve为cache数组
使用cache storage与service worker建立离线应用
在页面脚本中添加如下代码1
2
3
4//注册一个serviceWorker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw-demo-cache.js');
}
在sw-demo-cache.js中添加如下代码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
35
36
37
38
39
40
41
42
43
44
45
46
47// sw-demo-cache.js
var VERSION = 'v1';
// 缓存
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(VERSION).then(function(cache) {
//需要做缓存的文件
return cache.addAll([
'./start.html',
'./static/jquery.min.js',
'./static/mm1.jpg'
]);
})
);
});
// 缓存更新
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
// 如果当前版本和缓存版本不一致
if (cacheName !== VERSION) {
return caches.delete(cacheName);
}
})
);
})
);
});
// 捕获请求并返回缓存数据
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request).catch(function() {
return fetch(event.request);
}).then(function(response) {
caches.open(VERSION).then(function(cache) {
cache.put(event.request, response);
});
return response.clone();
}).catch(function() {
//没法从缓存和请求中得到资源时的处理办法
return caches.match('./static/mm1.jpg');
}));
});
FileSystem API
使用 FileSystem API,网络应用就可以创建、读取、导航用户本地文件系统中的沙盒部分以及向其中写入数据。但是目前FileSystem API并不普及,支持的只有chrome浏览器。FileSystem API提供了文件系统的管理、文件/目录的管理、文件的管理等API。刚兴趣的朋友可以阅读探索FileSystem API
参考文献
IndexedDB
使用IndexedDB
application cache api
Web SQL Database
CacheStorage 缓存存储
借助Service Worker和cacheStorage缓存及离线开发