如果资源文件有修改,则更新文件内容,同时修改资源文件名,如 common.v2.js,html页面也会引用新的资源文件名。
通过这种方式,实现了:缓存文件没有更新,则使用缓存;缓存文件有更新,则第一时间使用最新文件的目的。即上面说的第1、2条。第3、4条由于浏览器内部机制,目前还无法满足。
2.2 Dom Storage 存储机制
DOM 存储是一套在 Web Applications 1.0 规范中首次引入的与存储相关的特性的总称,现在已经分离出来,单独发展成为独立的 W3C Web 存储规范。 DOM 存储被设计为用来提供一个更大存储量、更安全、更便捷的存储方法,从而可以代替掉将一些不需要让服务器知道的信息存储到 cookies 里的这种传统方法。
上面一段是对 Dom Storage 存储机制的官方表述。看起来,Dom Storage 机制类似 Cookies,但有一些优势。
Dom Storage 是通过存储字符串的 Key/Value 对来提供的,并提供 5MB (不同浏览器可能不同,分 HOST)的存储空间(Cookies 才 4KB)。另外 Dom Storage 存储的数据在本地,不像 Cookies,每次请求一次页面,Cookies 都会发送给服务器。
DOM Storage 分为 sessionStorage 和 localStorage。localStorage 对象和 sessionStorage 对象使用方法基本相同,它们的区别在于作用的范围不同。sessionStorage 用来存储与页面相关的数据,它在页面关闭后无法使用。而 localStorage 则持久存在,在页面关闭后也可以使用。
Dom Storage 提供了以下的存储接口:
- interface Storage {
- readonly attribute unsigned long length;
- [IndexGetter] DOMString key(in unsigned long index);
- [NameGetter] DOMString getItem(in DOMString key);
- [NameSetter] void setItem(in DOMString key, in DOMString data);
- [NameDeleter] void removeItem(in DOMString key);
- void clear();
- };
复制代码
sessionStorage 是个全局对象,它维护着在页面会话(page session)期间有效的存储空间。只要浏览器开着,页面会话周期就会一直持续。当页面重新载入(reload)或者被恢复(restores)时,页面会话也是一直存在的。每在新标签或者新窗口中打开一个新页面,都会初始化一个新的会话。
- <blockquote><script type="text/javascript">
复制代码
当浏览器被意外刷新的时候,一些临时数据应当被保存和恢复。sessionStorage 对象在处理这种情况的时候是最有用的。比如恢复我们在表单中已经填写的数据。
把上面的代码复制到 session_storage.html(也可以从附件中直接下载)页面中,用 Google Chrome 浏览器的不同 PAGE 或 WINDOW 打开,在输入框中分别输入不同的文字,再点击“Save”,然后分别刷新。每个 PAGE 或 WINDOW 显示都是当前PAGE输入的内容,互不影响。关闭 PAGE,再重新打开,上一次输入保存的内容已经没有了。
Local Storage 的接口、用法与 Session Storage 一样,唯一不同的是:Local Storage 保存的数据是持久性的。当前 PAGE 关闭(Page Session 结束后),保存的数据依然存在。重新打开PAGE,上次保存的数据可以获取到。另外,Local Storage 是全局性的,同时打开两个 PAGE 会共享一份存数据,在一个PAGE中修改数据,另一个 PAGE 中是可以感知到的。
将上面代码复制到 local_storage.html 的页面中,用浏览器打开,pageLoadCount 的值是1;关闭 PAGE 重新打开,pageLoadCount 的值是2。这是因为第一次的值已经保存了。
用两个 PAGE 同时打开 local_storage.html,并分别交替刷新,发现两个 PAGE 是共享一个 pageLoadCount 的。
分析:Dom Storage 给 Web 提供了一种更录活的数据存储方式,存储空间更大(相对 Cookies),用法也比较简单,方便存储服务器或本地的一些临时数据。
从 DomStorage 提供的接口来看,DomStorage 适合存储比较简单的数据,如果要存储结构化的数据,可能要借助 JASON了,将要存储的对象转为 JASON 字串。不太适合存储比较复杂或存储空间要求比较大的数据,也不适合存储静态的文件等。
在 Android 内嵌 Webview 中,需要通过 Webview 设置接口启用 Dom Storage。
- WebView myWebView = (WebView) findViewById(R.id.webview);
- WebSettings webSettings = myWebView.getSettings();
- webSettings.setDomStorageEnabled(true);
复制代码
拿 Android 类比的话,Web 的 Dom Storage 机制类似于 Android 的 SharedPreference 机制。
2.3 Web SQL Database存储机制
H5 也提供基于 SQL 的数据库存储机制,用于存储适合数据库的结构化数据。根据官方的标准文档,Web SQL Database 存储机制不再推荐使用,将来也不再维护,而是推荐使用 AppCache 和 IndexedDB。
现在主流的浏览器(点击查看浏览器支持情况)都还是支持 Web SQL Database 存储机制的。Web SQL Database 存储机制提供了一组 API 供 Web App 创建、存储、查询数据库。
下面通过简单的例子,演示下 Web SQL Database 的使用。
将上面代码复制到 sql_database.html 中,用浏览器打开,可看到下面的内容。
官方建议浏览器在实现时,对每个 HOST 的数据库存储空间作一定限制,建议默认是 5MB(分 HOST)的配额;达到上限后,可以申请更多存储空间。另外,现在主流浏览器 SQL Database 的实现都是基于 SQLite。
分析:SQL Database 的主要优势在于能够存储结构复杂的数据,能充分利用数据库的优势,可方便对数据进行增加、删除、修改、查询。由于 SQL 语法的复杂性,使用起来麻烦一些。SQL Database 也不太适合做静态文件的缓存。
在 Android 内嵌 Webview 中,需要通过 Webview 设置接口启用 SQL Database,同时还要设置数据库文件的存储路径。
- WebView myWebView = (WebView) findViewById(R.id.webview);
- WebSettings webSettings = myWebView.getSettings();
- webSettings.setDatabaseEnabled(true);
- final String dbPath = getApplicationContext().getDir(“db”, Context.MODE_PRIVATE).getPath();
- webSettings.setDatabasePath(dbPath);
复制代码
Android 系统也使用了大量的数据库用来存储数据,比如联系人、短消息等;数据库的格式也 SQLite。Android 也提供了 API 来操作 SQLite。Web SQL Database 存储机制就是通过提供一组 API,借助浏览器的实现,将这种 Native 的功能提供给了 Web App。
2.4 Application Cache 机制
Application Cache(简称 AppCache)似乎是为支持 Web App 离线使用而开发的缓存机制。它的缓存机制类似于浏览器的缓存(Cache-Control 和 Last-Modified)机制,都是以文件为单位进行缓存,且文件有一定更新机制。但 AppCache 是对浏览器缓存机制的补充,不是替代。
先拿 W3C 官方的一个例子,说下 AppCache 机制的用法与功能。
- <blockquote><!DOCTYPE html>
复制代码
上面 HTML 文档,引用外部一个 JS 文件和一个 GIF 图片文件,在其 HTML 头中通过 manifest 属性引用了一个 appcache 结尾的文件。
我们在 Google Chrome 浏览器中打开这个 HTML 链接,JS 功能正常,图片也显示正常。禁用网络,关闭浏览器重新打开这个链接,发现 JS 工作正常,图片也显示正常。当然也有可能是浏览缓存起的作用,我们可以在文件的浏览器缓存过期后,禁用网络再试,发现 HTML 页面也是正常的。
通过 Google Chrome 浏览器自带的工具,我们可以查看已经缓存的 AppCache(分 HOST)。
上面截图中的缓存,就是我们刚才打开 HTML 的页面 AppCache。从截图中看,HTML 页面及 HTML 引用的 JS、GIF 图像文件都被缓存了;另外 HTML 头中 manifest 属性引用的 appcache 文件也缓存了。
AppCache 的原理有两个关键点:manifest 属性和 manifest 文件。
HTML 在头中通过 manifest 属性引用 manifest 文件。manifest 文件,就是上面以 appcache 结尾的文件,是一个普通文件文件,列出了需要缓存的文件。
上面截图中的 manifest 文件,就 HTML 代码引用的 manifest 文件。文件比较简单,第一行是关键字,第二、三行就是要缓存的文件路径(相对路径)。这只是最简单的 manifest 文件,完整的还包括其他关键字与内容。引用 manifest 文件的 HTML 和 manifest 文件中列出的要缓存的文件最终都会被浏览器缓存。
完整的 manifest 文件,包括三个 Section,类型 Windows 中 ini 配置文件的 Section,不过不要中括号。
manifest 文件本身不能被缓存,且 manifest 文件的更新使用的是浏览器缓存机制。所以 manifest 文件的 Cache-Control 缓存时间不能设置太长。
另外,根据官方文档,AppCache 已经不推荐使用了,标准也不会再支持。现在主流的浏览器都是还支持 AppCache的,以后就不太确定了。
在Android 内嵌 Webview中,需要通过 Webview 设置接口启用 AppCache,同时还要设置缓存文件的存储路径,另外还可以设置缓存的空间大小。
- WebView myWebView = (WebView) findViewById(R.id.webview);
- WebSettings webSettings = myWebView.getSettings();
- webSettings.setAppCacheEnabled(true);
- final String cachePath = getApplicationContext().getDir(“cache”, Context.MODE_PRIVATE).getPath();
- webSettings.setAppCachePath(cachePath);
- webSettings.setAppCacheMaxSize(5<em>1024</em>1024);
复制代码
2.5 Indexed Database
IndexedDB 也是一种数据库的存储机制,但不同于已经不再支持的 Web SQL Database。IndexedDB 不是传统的关系数据库,可归为 NoSQL 数据库。IndexedDB 又类似于 Dom Storage 的 key-value 的存储方式,但功能更强大,且存储空间更大。
IndexedDB 存储数据是 key-value 的形式。Key 是必需,且要唯一;Key 可以自己定义,也可由系统自动生成。Value 也是必需的,但 Value 非常灵活,可以是任何类型的对象。一般 Value 都是通过 Key 来存取的。
IndexedDB 提供了一组 API,可以进行数据存、取以及遍历。这些 API 都是异步的,操作的结果都是在回调中返回。
下面代码演示了 IndexedDB 中 DB 的打开(创建)、存储对象(可理解成有关系数据的”表“)的创建及数据存取、遍历基本功能。
- <blockquote><script type="text/javascript">
复制代码
将上面的代码复制到 indexed_db.html 中,用 Google Chrome 浏览器打开,就可以添加、查询数据。在 Chrome 的开发者工具中,能查看创建的 DB 、存储对象(可理解成表)以及表中添加的数据。
IndexedDB 有个非常强大的功能,就是 index(索引)。它可对 Value 对象中任何属性生成索引,然后可以基于索引进行 Value 对象的快速查询。
要生成索引或支持索引查询数据,需求在首次生成存储对象时,调用接口生成属性的索引。可以同时对对象的多个不同属性创建索引。如下面代码就对name 和 email 两个属性都生成了索引。
- var objectStore = thisDB.createObjectStore(“people”,{ autoIncrement:true });
- //first arg is name of index, second is the path (col);
- objectStore.createIndex(“name”,”name”, {unique:false});
- objectStore.createIndex(“email”,”email”, {unique:true});
复制代码
生成索引后,就可以基于索引进行数据的查询。
- <p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">function getPeopleByNameIndex(e)</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">{</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">var name = document.querySelector("#name1").value;</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">
- </p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">var transaction = db.transaction(["people"],"readonly");</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">var store = transaction.objectStore("people");</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">var index = store.index("name");</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">
- </p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">//name is some value</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">var request = index.get(name);</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">request.onsuccess = function(e) {</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> var result = e.target.result;</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> if(result) {</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> var s = "<p><h2>Name "+name+"</h2><p>";</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> for(var field in result) {</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> s+= field+"="+result[field]+"<br/>";</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> }</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> s+="</p>";</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> } else {</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> document.querySelector("#status3").innerHTML = "<h2>No match!</h2>";</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> }</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;"> }</p><p style="margin-top: 0.5em !important; margin-bottom: 0.5em !important;">}</p>
复制代码
分析:IndexedDB 是一种灵活且功能强大的数据存储机制,它集合了 Dom Storage 和 Web SQL Database 的优点,用于存储大块或复杂结构的数据,提供更大的存储空间,使用起来也比较简单。可以作为 Web SQL Database 的替代。不太适合静态文件的缓存。