JavaScript高级程序设计-第三版(离线应用与客户端存储)

二十三、离线应用与客户端存储

23.1 离线检测

  • navigator.onLine 属性
    • 属性值为true表示设备能上网
  • window对象的online 事件
  • window对象的offline 事件

23.2 应用缓存

  • application cache(appcache)
    • 启用缓存
      • 在页面中的 <html> 元素上增加 manifest 特性,并与缓存清单(cache manifest) 文件关联
    • 文档加载
      • 浏览器会自动缓存添加有 manifest 特性的页面
      • 清单文件修改后浏览器会自动更新缓存
        • 先将新的缓存文件下载到临时空间
        • 待所有缓存文件下载完毕后再更新到正式缓存中
<html manifest="/offline.manifest" >
...
</html>


<!--offline.manifest-->
---------------
CACHE MANIFEST
#Comment

file.js
file.css
---------------
  • applicationCache对象
    • status属性
      • 0 无缓存
      • 1 闲置
      • 2 检查中,即正在下载描述文件并检查更新
      • 3 下载中,即应用缓存正在下载描述文件中指定的资源
      • 4 更新完成,所有新的缓存资源都已下载完毕,待执行更新
      • 5 废弃,即描述文件不存在
    • 事件
      • checking 查找更新时触发
      • error
      • noupdate
      • downloading 开始下载缓存文件
      • progress 文件下载中持续触发
      • updateready 文件下载完毕
      • cached 缓存可用
    • 方法
      • update()
        • 可手动触发浏览器检查更新
      • swapCache()
        • 启用下载好的新缓存

23.3 数据存储

23.3.1 Cookie

  • 服务器将会话信息保存到客户端,之后每次客户端发送请求时都会携带该信息
    • 服务器发送 Set-Cookie HTTP头作为响应
    • 客户端为每次请求添加Cookie HTTP头
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value
GET /index.html HTTP/1.1
Cookie: name=value
Other-header: other-header-value
23.3.1.1 限制
  • 只在请求创建该Cookie的域名时才会携带该Cookie
  • 浏览器对Cookie的数量以及单个Cookie的大小有限制
23.3.1.2 cookie的构成
  • 名称
    • 不区分大小写
    • 在向该域发送请求时会携带cookie
  • 路径
    • 将携带cookie的请求限制在某个域下的某个路径,只有向该路径发送请求才会携带cookie
  • 失效时间
    • 表示cookie何时应该被删除的时间戳,默认会话结束后删除
  • 安全标志
    • 设定后只有使用SSL连接才会将cookie发送到服务器
    • 设定方式为在响应头中加入"secure"
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; domain=.wrox.com; path=/; secure
Other-header: other-header-value
23.3.1.3 Javascript中的cookie
  • document.cookie
    • 读取
      • 返回所有cookie组成的字符串,例如,“name1=value1;name2=value2”
      • 字符串经过了URL编码
    • 写入
      • 给document.cookie赋值时,只要键是新的键,就是添加到cookie集合中,重复键则为覆盖。例如,document.cookie = “name=Nicholas”
    • 删除
      • 将失效时间设置为过期时间即可
    • 由于读取时获取的是全部cookies,而写入时需要进行URL编码,所以可以自定义工具对象,提供读取、写入、操作的简便方法
23.3.1.4 子cookie
  • 为了解决浏览器单个域名下cookie个数的限制,将多个cookie放入同一个key下的value中
    • 例如,name=“name1=value1&name2=value2”
  • 读取
    • 先从document.cookie中截取主cookie
      • var cookieStart = document.cookie.indexOf(cookieName)
      • var cookieEnd = document.cookie.indexOf( " ; " , cookieStart )
    • 从主cookie的值中截取所有子cookie作为结果对象的属性
      • var subCookies = cookieValue.split("&")
      • var parts = subCookies[i].split("=")
      • var result = {}; result[ parts[0] ] = parts[1]
    • 通过子cookie名称与结果对象的属性名进行匹配,获取子cookie
      • var subCookie = result[ subName ]
  • 写入
    • 先获取主cookie下的所有子cookie的结果对象
    • 将写入的值加入到结果对象中
    • 将所有子cookie形成的结果对象转化为"name=value"形式的数组
      • 循环实例属性 hasOwnProperty()
    • 将数组拼接成以"&"为连接符的字符串
    • 替换主cookie
  • 删除
    • 先获取主cookie下的所有子cookie的结果对象
    • 删除结果对象中的某个属性
    • 将所有子cookie形成的结果对象转化为"name=value"形式的数组
      • 循环实例属性 hasOwnProperty()
    • 将数组拼接成以"&"为连接符的字符串
    • 替换主cookie
23.3.1.5 关于cookie的思考
  • cookie信息越大,完成对服务器请求的时间也就越长
  • 每次请求都会携带给服务器

23.3.2 IE用户数据

  • 微软通过自定义行为引入持久化用户数据
  • 必须在元素上使用CSS指定userData行为
    • <div id=“dataStore” style=“behavior:url(#default#userData)”></div>
  • 写入数据
    • 通过DOM给元素设置属性
      • dataStore.setAttribute(“name”,“Nicholas”)
    • 调用save()方法将数据提交到浏览器缓存中,并设置数据空间的名称
      • dataStore.save(“BookInfo”);
  • 读取数据
    • 将数据空间的数据载入到元素
      • dataStore.load(“BookInfo”);
    • 通过DOM读取元素属性
      • dataStore.getAttribute(“name”)
  • 删除数据
    • 删除属性
      • removeAttribute(“name”)
    • 调用save()提交更改

23.3.3 Web存储机制

  • Web Storage
    • 特点
      • 数据需要控制在客户端上,无须持续将数据发送给服务器
      • 存储容量大
    • 类型
      • sessionStorage
      • globalStorage
      • 作为window对象的属性
23.3.3.1 Storage类型
  • 只能存储字符串
  • 方法
    • clear()
    • getItem(name)
    • key(index)
      • for遍历时使用
    • removeItem(name)
    • setItem(name, value)
23.3.3.2 sessionStorage对象
  • sessionStorage对象
    • 特点
      • 存储特定于某个会话的数据,在浏览器关闭后数据消失
      • 刷新后依旧可用
      • 只能由最初给对象存储数据的页面访问
      • 保存的数据在本地运行时不可用
    • 写入方式
      • 部分浏览器支持同步,部分浏览器支持异步
      • 可以调用commit()方式强制写入磁盘
        • 在commit()方法前可以调用begin()方法,通过事务的方式进行数据保存
23.3.3.3 globalStorage对象
  • globalStorage
    • 特点
      • 跨越会话存储数据
      • 需要先指定可以访问的域,通过域名、协议和端口匹配
      • 可以一直保留在磁盘上
    • 读写方式
      • globalStorage[“wrox.com”].name = “Nicholas” ;
      • var name = globalStorage[“wrox.com”].name;
        • globalStorage[“wrox.com”]才是Storage的实例
        • wrox.com及其子域可以访问该存储空间
23.3.3.4 localStorage对象
  • localStorage与globalStorage类似,但不能自定义指定访问域
    • 规则事先设定好了,页面必须来自同一域名、协议、端口
    • 相当于globalStorage对象将域名限定为location.host
23.3.3.5 storage事件
  • 对Storage对象进行任何修改,都会触发storage事件
  • event对象实例属性
    • domain
    • key
    • newValue
    • oldValue
23.3.3.6 限制
  • 存储空间大小以每个来源(协议、域、端口)为单位进行限制

23.3.4 IndexedDB

  • Indexed Database API
    • 浏览器中保存结构化数据的数据库
    • 替代Web SQL Database API
    • 保存和读取JavaScript对象,支持查询及搜索
    • 异步进行,每次操作需要注册事件处理程序以处理结果
    • IndexedDB为API宿主的全局对象
23.3.4.1 数据库
  • IndexedDB
    • 特点
      • 使用对象保存数据
      • 一组位于相同命名空间下的对象的集合
    • 基本使用
      • indexedDB.open(databaseName)
        • 已存在名称为databaseName的数据库,则打开
        • 否则,则创建
        • 二者都会返回 IDBRequest 对象
      • 可以在IDBRequest对象上注册事件处理程序对请求结果进行处理
        • 请求成功 onsuccess
          • event.target.result中会保存IDBdatabase实例
          • 可以设置IDBdatabase实例的版本号
            • 调用database.setVersion(versionStr)
            • 同样返回IDBRequest对象
        • 请求失败 onerror
          • event.target.errorCode 中会保存错误码
var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB ;
var request , database ;
request = indexedDB.open("admin");
request.onsuccess = function(event){
	database = event.target.result ;
};
request.onerror = function(event){
	alert("Something bad happened while trying to open" + 
	event.target.errorCode);
};
23.3.4.2 对象存储空间
  • 对象存储空间
    • 存储的对象的集合,或者理解为数据库表
    • 要保存的对象,可以理解为表中的记录
  • 创建存储空间
    • database.createObjectStore( storeName, { keyPath: propertyName } )
      • keyPath为主键
  • 添加、修改数据
    • store.add()
      • 添加已存在的键值会报错
    • store.put()
      • 添加已存在的键值会覆盖原对象
23.3.4.3 事务
  • 除了创建存储空间,接下来的任何操作都需要通过事务来完成
  • 创建事务
    • database.transaction()
      • 传入参数
        • 参数一:要访问的存储空间
          • 可以是多个,传入字符串数组
        • 参数二:访问数据的方式
          • READ_ONLY(0)
          • READ_WRITE(1)
          • VERSION_CHANGE(2)
  • 访问存储空间
    • transaction.objectStore( storeName )
      • 可以在返回值上进行add()、put()、delete( keyPath )、get( keyPath )、clear()等操作
      • 支持complete事件,但在event对象中无法访问到结果数据
23.3.4.4 使用游标查询
  • 游标
    • 查找多个对象时使用
    • 指向结果集的第一项的指针
  • 创建游标
    • store.openCursor()
    • success事件中event.target.result保存了IDBCursor实例,可以访问下一个对象
    • IDBCursor
      • direction 游标移动的方向
        • IDBCursor.NEXT(0)
        • IDBCursor.NEXT_NO_DUPLICATE(1) 下一个不重复的项
        • IDBCursor.PREV(2)
        • IDBCursor.PREV_NO_DUPLICATE(3)
        • 在创建游标时可以作为第二个参数对游标进行设定
      • key
      • value
      • primaryKey 游标使用的键(对象键或者索引键)
  • 通过游标对存储空间进行删改查的操作
var request = store.openCursor();
request.onsuccess = function(event){
	var cursor = event.target.result,
		value,
		updateRequest;
	if(cursor){
		//查询
		console.log("Key:" + cursor.key + ",Value:"+
			JSON.stringify(cursor.value) );
			
		if(cursor.key =="foo"){
			//修改
			value = cursor.value;
			value.password = "magic!";
			updateRequest = cursor.update(value);
			updateRequest.onsuccess = function(event){
				...
			};
			
			//删除
			deleteRequest = cursor.delete();
			deleteRequest.onsuccess = function(event){
				...
			};
	}
};
  • 游标的移动
    • continue( key ) 不指定键值时为移动到下一项
    • advance( count )
    • 游标使用移动之前的相同的请求,事件处理程序也会重用
var request = store.openCursor();
request.onsuccess = function(event){
	var cursor = event.target.result;
	if(cursor){
		console.log("Key:" + cursor.key + ",Value:"+
			JSON.stringify(cursor.value) );
		cursor.continue();
	}
	else{
		console.log("Done!");
	}
};			
23.3.4.5 键范围
  • 通过键范围可以获得指向部分符合条件的结果集
  • 键范围
    • IDBKeyRange类型
    • 定义方式
      • IDBKeyRange.only( key )
      • IDBKeyRange.lowerBound( key , bool )
        • true 表示跳过该键,取其下一项
      • IDBKeyRange.upperBound( key , bool )
      • IDBKeyRange.bound( lowerKey , upperKey , lowerbool , upperbool)
var store = db.transaction("users").objectStore("users"),
	range = IDBKeyRange.bound("007" , "ace"),
	request = store.openCursor(range);
request.onsuccess = function(event){
	...
};
23.3.4.6 设定游标方向
  • store.openCursor( null, IDBCursor.NEXT_NO_DUPLICATE)
  • cursor.continue() 和 cursor.advance()只触发移动,方向由创建游标时进行设定
23.3.4.7 索引
  • 通过索引可以为存储空间指定多个键
  • 索引
    • 创建索引
      • store.createIndex( IndexName, PropertyName, options )
        • options
          • { unique:false }
      • IDBIndex 类型
        • name 即 IndexName
        • keyPath 即 PropertyName
        • objectStore
        • unique bool类型
    • 获取已经存在的索引
      • store.index( IndexName )
    • 获取存储空间的所有索引的名称集合
      • store.indexNames属性
    • 通过索引获取主键
      • index.getKey( IndexName )
    • 删除索引
      • store.deleteIndex( IndexName )
  • 创建游标
    • index.openCursor()
      • event.result.key 为索引键
    • index.openKeyCursor()
      • event.result.key 为索引键
      • event.result.value 为主键
23.3.4.8 并发问题
  • 当两个不同的标签页打开同一个页面,一个标签页试图更新数据库(版本),而另一个页面打开了数据库,则会发生并发问题
    • 解决方式
      • 利用versionchange事件在版本更新前将已打开的数据库关闭,只保留发出更新版本指令的页面的数据库连接
      • 对于发出更新版本指令的页面,利用blocked事件提醒用户关闭其他标签页
23.3.4.9 限制
  • IndexedDB数据库只能由同源页面操作
  • 数据库占据的磁盘空间有限制
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值