浏览器数据持久化存储技术解析

打开Chrome浏览器的调试模式,Application就列举了现代浏览器的8种缓存机制:HTTP文件缓存,LocalStorage,SessionStorage,indexDB,webSQL,Cookie,CacheStorage,ApplicationCache.

从网址到网页展示

我们先看一个问题,从我们打开浏览器地址栏输入一个网址,到浏览器展示网页内容的这段时间,浏览器和服务端都发生了什么事情?

  • 在接受到用户输入的网址后,浏览器会开启一个线程来处理这个请求,对用户输入的 URL 地址进行分析判断,如果是HTTP协议就按照HTTP方式来处理.
  • 调用浏览器引擎中的对应方法,比如 WebView 中的 loadUrl 方法,分析并加载这个 URL 地址.
  • 通过 DNS 解析获取该网站对应的 IP 地址,查询完成后连同浏览器的 Cookies,userAgent 等信息向网站目的地 IP 发送 Get 请求.
  • 进行 HTTP 协议会话,浏览器客户端向 web 服务器发送报文.
  • 进入网站后台上的 web 服务器处理请求, 如 Apache,Tomcat, Node.js服务器.
  • 进入部署好的后端应用,如 PHP, java, Javascript, Python等后端程序,找到对应的请求处理逻辑,这期间可能会读取服务器缓存或查询数据库等.
  • 服务器处理请求并返回响应报文,此时如果浏览器访问过该页面,缓存上有对应资源,会与服务器最后修改记录对比,一致则返回304,否则返回200与对应的内容.
  • 浏览器开始下载 HTML 文档(响应报文状态码为200时) 或者从本地缓存读取文件内容(浏览器缓存有效或响应报文状态码为304时).
  • 浏览器根据下载接收到的 HTML 文件解析结构建立 DOM文档树,并根据 HTML 中的标记请求下载指定的MIME文件(如CSS,JAvaScript脚本等),同时设置缓存等内容.
  • 页面开始解析渲染DOM,CSS根据规则解析并结合DOM文档树进行网页内容布局和绘制渲染,JavaScript根据DOM API 操作DOM,并读取浏览器缓存,执行事件绑定等,页面整个展示过程完成.

HTTP文件缓存

HTTP文件缓存是基于HTTP协议的浏览器端文件级缓存机制,在文件重复请求的情况下,浏览器可以根据HTTP响应的协议头信息判断是从服务器端请求文件还是本地读取文件.以下是 文件缓存的过程.

1. 浏览器会先查询Cache-Control来判断内容是否过期,如果未过期,直接读取浏览器端缓存文件不发送HTTP请求,否则进入下一步.
2. 在浏览器端判断上次文件返回头中是否含有Etag信息,有则连同If-None-Match一起向服务器发生请求,服务端判断Etag未修改则返回状态304,修改则返回200,否则进入下一步.
3. 在浏览器端判断上次文件是否含有Last-Modified信息,有则一起向服务器发送请求,服务器判断是否失效,失效返回200,未失效返回304.
4. 如果Etag和Last-Modified都不存在,则向服务器请求内容.

在HTML中我们添加的meta标签中的ExpiresCache-Control,且一般Cache-Control设置的是秒,如果以上两个同时设置,只要Cache-Control的设置生效.

1
2
<meta http-equiv="Expires" content="Mon, 20 Jul 2016 23:00:00 GMT"/>
<meta http-equiv="Cache-Control" content="max-age=7200">

同时服务端也要设置静态资源的缓存时间.我们可以结合Koa-static中间件设置实现.

1
2
3
4
5
const static = require('koa-static')
const app = koa()
app.use(static('./pages',{
maxage: 7200
}))

localStorage

localStorage是HTML5的一种本地缓存方案,目前主要用于浏览器端保存体积较大的数据(如AJAX返回结果等).但它在各版本浏览器的长度限制不一.它的核心API只有4个.

1
2
3
4
localStorange.setItem(key,value)//设置存储记录
localStorage.getItem(key)//获取储存记录
localStorage.removeItem(key)//删除记录
localStorage.clear()//清空

LocalStorage只支持简单数据类型的读取,为方便读取对象等格式内容,通常需要进行一层安全封装再引入使用.

sessionStorage

sessionStorage和LocalStorage功能类似,但sessionStorange在浏览器关闭时会自动清空.它的API和LocalStorage的API完全相同,但由于不能持久化数据存储,因此使用场景较少.

cookie是网站为了辨别用户身份或session追踪而存储在用户浏览器的数据,cookie一般会通过HTTP请求到服务器端.一条cookie主要由键,值,域,过期时间和大小组成,一般用于保存用户的网站认证信息.通常最大限制为4KB.

cookie分为两种,sessionCookie和持久型Cookie.前者一般不设置过期时间,表示与浏览器会话期间保存在内存中,持久性Cookie会设置过期时间保存在本地硬盘中,知道过期或清空才失效.

Cookie设置中有个HttpOnly参数,浏览器端通过doucument.cookie是读取不到HttpOnly类型的Cookie的,只能通过HTTP请求头发送到服务器进行读写操作.这样可以避免服务器端的Cookie记录被js修改,保证了服务端验证cookie的安全性.

WebSQL

webSQL是浏览器端用于存储大量数据的缓存机制,以一个独立浏览器端数据存储规范的形式出现.它在HTML5前就已经出现,是单独的规范,它将数据以数据库二维表的形式存储在客户端,并且允许SQL语句的查询.

webSQL的API主要包含上个核心方法:openDatabase(),transaction()和executeAql().

1
2
3
4
5
6
7
//openDatabase()打开已经存在的数据库,不存在就创建.他的五个参数是数据库名,版本号,描述,数据库大小,创建回调.
let db = openDatabase('mydatabase','1.0','test',2*1024*1024)
db.transaction(function(table){
table.executeSql('INSERT INTO t1 (id,msg) VALUES (1,"hello")')
})
//transaction方法允许我们根据情况控制事物提交或回滚.
//executeSql用于执行真实的SQL查询语句.

IndexDB

IndexDB也是客户端存储大量结构化数据并且能在这些数据上使用索引进行高性能检索的一套API.由于webSQL不是HTML5规范,一般推荐使用IndexDB进行大量数据存储,其基本实现和webSQL类似.

1
2
3
4
5
if(database){
database.transaction(function(tx){
tx.executeSql('INSERT INTO t1 (id,msg) VALUES (1,"hello")')
})
}

Application Cache

Application Cache是一种允许浏览器通过manifest配置文件在本地有选择性的存储js,css,图片等静态资源的缓存机制.当页面不是首次打开时,通过一个特定的manifest文件配置描述选择性新读取本地ApplicationCache的文件.所以它具有离线浏览,快速加载,服务器载荷小的优势.它的文件访问及更新机制如下:

1. 判断是否是第二次加载页面.
2. 是的话访问AppCache.
3. 检查manifest文件是否更新
4. 无更新从AppCache读取,有更新则重新拉取并更新AppCache.

使用方式

1
2
3
4
5
6
7
8
<html manifest="app.manifest">
//对应的描述文件如下
CACHE MANIFEST
#VERSION 1.0
CACHE:
xxx.css
XXX.js
XXX.png

浏览器也可以根据window.applicationCache来对其进行控制.

尽管ApplicationCache的实现很方便,但是已经开始被标准弃用,渐渐将会由ServiceWorkers来代替.总之,ApplicationCache仍是一个不成熟的本地缓存解决方案.

cacheStorage

cacheStorage是ServiceWorkers规范中定义的,用于保存每个ServiceWorker声明的Cache对象,是未来可代替ApplicationCache的离线方案.

CacheStorage在浏览器端为window.caches,有open,match,has,delete,keys五个API.

1
2
3
4
5
caches.has();//检查如果包含cache对象,返回一个promise
caches.open();//打开一个cache对象,返回一个promsie
caches.delete();//删除一个cache对象,返回一个promise
caches.keys();//含有keys中字符串的任意一个,返回一个promise
caches.match();//匹配key中含有该字符的cache,返回一个promise

Flash缓存

Flash缓存主要基于网页端Flash,具有读写浏览器本地目录的功能,同时也可以向js提供调用的API,这样页面就可以通过js调用Flash读写本地指定的磁盘目录,达到本地数据缓存的目的.

本文摘自张成文编著<<现代前端技术解析>>,详情请点击张成文的Github