[关闭]
@lsmn 2016-05-06T03:14:32.000000Z 字数 4952 阅读 2291

精通客户端存储技术

1试译


正文

第一章 客户端数据存储简介

过去十年中,浏览器已经发展成为一个强大的工具,这是一个缓慢的过程,伴随着许多成长之痛。现在,增强型布局控件、3D图形和游戏、甚至是音乐都可以在小而古老的浏览器中实现。客户端数据存储是一个更加令人兴奋的特性,虽然相比之下,它没那么华丽(没有别的意思)。但我们为什么这样说呢?

浏览Web的“经典”过程从一开始就没有变过:浏览器请求一个URL,Web服务器返回请求的内容,然后浏览器请求更多的内容,而服务器就返回更多的内容。

当然,你可以让情况更复杂一些,引入JavaScript和AJAX。但是,即使是在一个精心设计的Web 2.0应用程序中,浏览器还是会一次又一次地向服务器请求信息。之所以会这样,是因为——无论出于什么目的——浏览器是一个健忘症患者。它知道的所有东西都必须从服务器习得。

虽然一般而言确是如此,但那忽视了一个功能强大的替代方案——将数据存储在浏览器中,让它可以跳过向服务器请求信息的过程,而只从用户的本地机器上获取数据。它甚至还允许操作那些数据,用于任何可能合理的用途。稍后,数据可以发回服务器用于更新。

总之,这让浏览器可以:
直接访问数据。虽然使用AJAX获取数据的速度通常已经快了很多,但将数据存储在本地机器上会让数据访问速度更快;
节省网络流量。浏览器获取一次数据,只要有用就一直保存着,而不必不断地从服务器获取数据。这使得……
减轻服务器的压力。如果服务器不断地响应请求,并从数据库服务器获取数据,那么服务器会负担过重。减少请求次数,可以减少服务器的工作。
最后,数据存储在本地,创建完全离线的应用程序变得更加可行。

当然,并非一切都如此美好。将数据转移到浏览器也有以下几点不足:
没有任何同步支持。设想一下,你已经将数据复制到了浏览器。如何处理数据同步呢?如果出现冲突会怎样?本书所谈及的核心技术,没有一项支持任何有关同步处理的概念。不过,你会发现,像PouchDB这样的库内置了同步功能;
存储限制模糊。作为开发人员,我们讨厌模糊。我们希望准确地知道可以使用多少资源。遗憾地是,对于本书将要谈及的其中许多技术而言,这个限制——以及打破限制的后果——有点模糊;
最后,虽然本书谈及的技术功能非常强大,但它们并不能取代纯正的数据库服务器。数据库服务器针对处理大量数据的任务进行了特别仔细的优化,并提供了查找数据的方法。本书将要谈及的方案无疑能够存储数据,但同时,它们并不像一个嵌入式Oracle服务器。(虽然那可能是一件好事。)

本书将讨论各种客户端存储技术。对于每一种技术,本书还会清楚公正地讨论它们实际上得到了多大程度的支持。你将会看到API示例以及演示程序,它们可以帮助你学习如何使用API。最后,我们将看几个旨在简化客户端存储的库。做好准备——让我们浏览一些更为实用的Web特性,这将是一段有趣、有时又有点艰难的旅途。

第二章 使用Cookie

Cookie?真的吗?

在2015年一本有关现代Web开发的书里讨论Cookie,我真是有点不好意思,但是,这是开发人员如今可以使用的最古老同时也是最稳定的客户端存储形式。当然,Cookie不是最好的方法,我几乎从来不建议使用它,但是,它是一种选择,在将来的某个时候,你也许不得不使用(或修改)使用了Cookie的代码。

Cookie于1994年在Netscape的一个Beta版本中引入。它通过随HTTP请求和响应一起发送的HTTP Header值发挥作用。众所周知,每当浏览器请求一个资源,就会有一组Header随请求一起发送。那些Header包含各种类型的数据,其中包括有关浏览器的信息以及它需要的数据形式。反过来,服务器也会往回发送Header。基本上,每次你看到一个Web页面在浏览器中渲染,就有一组你看不到的Header被发送。(当然,你可以使用浏览器工具查看它们。它们并没有被隐藏得“无法看到”,只是在默认情况下看不到。)

Cookie使用HTTP Header发送,具体来说是名为“Cookie”的HTTP Header,由浏览器发送到服务器,又从服务器发送到浏览器。现在,你会发现这里有个问题。如果使用客户端存储的一个好处是不用通过网络发送数据,那么来回发送Cookie不是抵消了这种好处吗?完全正确。这就是我通常不建议使用Cookie的另一个原因。

在默认情况下,浏览器没有限制它可以拥有的Cookie数量。以前,每个域名最多只能有20个Cookie,但如今的浏览器似乎已经去掉了这个限制。(顺便说一句,我曾经在Chrome浏览器中设置了400多个Cookie,它仍然可以正常运行。不过,当它发出请求时,Web服务器开始抛出错误。因此,在这种情况下,这是一个Web服务器有限制的问题,而不是浏览器本身有限制。但请不要使用400个Cookie。)研究,或者换句话说,谷歌搜索似乎表明,每个域名50个、大小总计4KB的Cookie是安全的。

Cookie是域名唯一的。就是说,在foo.com上设置的Cookie值在goo.com上不可见。这样很好,因为你不会希望其他网站影响你在自己的网站上使用Cookie。Cookie也可以是子域名唯一的。例如,app.foo.com是Foo网站的一个唯一子域名。你可以创建只有app.foo.com可以读取的Cookie,也可以创建www.foo.com和app.foo.com都可以读取的Cookie。

更复杂一点儿,你还可以创建只对特定路径有效的Cookie。就是说,你可能希望创建只有foo.com/app可见的Cookie。

最后,你可以创建只对网站的安全(HTTPS)版本有效的Cookie。显然,选用哪种方案取决于应用程序的用途以及你认为哪里需要Cookie值。

在设置了Cookie的地方,你还可以指定Cookie的有效时间。对此,你有以下几个选项:

使用Cookie

Cookie没有API。要使用Cookie,只需要在代码中访问document.cookie对象。例如,你可以像下面这样创建一个Cookie:

document.cookie = "nameOfCookie=value";

上面的例子创建了一个名为nameOfCookie的Cookie,并将它的值定义为value。你可以使用“name=value”同时定义名称和值。下面是一个真实的例子:

document.cookie = "name=Raymond";

在上面的例子中,我简单设置了一个名为name值为Raymond的Cookie。值必须是URL安全的,就是说,如果你想动态定义Cookie,就需要使用一个类似encodeURIComponent这样的辅助函数:

name = "Raymond Camden";
document.cookie = "name=" + encodeURIComponent(name);

目前为止,一切顺利。但这就是事情变得有点古怪的地方。你可能想知道,如何设置多个Cookie。如果按字面理解直接对document.cookie进行多次设置,那能够实现。例如:

document.cookie = "name=Raymond";
document.cookie = "age=43";

在这段示例代码中,我们确实是创建了2个Cookie,而不是一个。我觉得这是完全错误的,但是我们必须适应这种定义方式。

好了,Cookie就是那样创建并赋值的,但是,我提到过的所有其他元数据呢——像定义Cookie在哪里可见以及它存在多长时间?在Cookie值后面使用一个分号可以追加元数据。下面是一个例子:

document.cookie = "name=Raymond; expires=Fri, 31 Dec 9999 23:59:59 GMT";

这个例子指明了Cookie何时过期。我们可以进一步扩展,指定该Cookie只对一个子域名有效:

document.cookie = "name=Raymond; expires=Fri, 31 Dec 9999 23:59:59 GMT;
domain=app.foo.com";

你已经了解了如何设置Cookie。当你不这样指定元数据时,Cookie默认只对当前域名的当前路径有效(你可能不希望这样),过期时间是当前会话。

读取Cookie

读取Cookie多少简单一些——取决于你对字符串解析的习惯程度。没有API可以用来获取“一个”Cookie。不过,你只需要简单地读取document.cookie就可以了。这样,你就可以获取到特定网站的所有Cookie。下面是从CNN获取的document.cookie值:

"_cb_ls=1;
_chartbeat2=Dlxk2YDHxyg1BXCry6.1426601000831.1439508384927.0000000000000001;
Akamai_AnalyticsMetrics_clientId=89E881222E0BD593DF2468758F328F689C36BAC1;
octowebstatid=16ppgnhrso5f2frjuvq5; ug=55cd27810eb00b0a3c6ac33c7d05339d; ugs=1;
__CG=u%3A2449373858398994400%2Cs%3A72001958%2Ct%3A1439508379253%2Cc%3A1%2Ck%3
Awww.cnn.com/19/19/54%2Cf%3A0%2Ci%3A0; __CT_Data=gpv=10;
__gads=ID=3d001e8bba3c7c6d:T=1426601001:S=ALNI_MYWNYv1SRt0tx7LQ2AzdSESOBygNA;
__vrf=1439508379290061VnNeHVWPiIjkcWMeUjRWpppwsPktE;
grvinsights=a5a942f8e7c604d573496053d63f590c; optimizelyBuckets=%7B%7D;
optimizelyEndUserId=oeu1426600996913r0.5135214943438768;
optimizelySegments=%7B%22170962340%22%3A%22false%22%2C%22171657961%22%3A%22
safari%22%2C%22172148679%22%3A%22none%22%2C%22172265329%22%3A%22direct%22%7D;
RT=sl=1&ss=1439508375405&tt=9387&obo=0&bcn=%2F%2F36f11e49.mpstat.us%2F&sh=1439
508384794%3D1%3A0%3A9387&dm=cnn.com&si=5be398d8-bb51-42ea-8128-6d4251e47ada;s_cc=true;s_fid=5324AC5D0F8323AB-3F26DEB602CBB276; s_ppv=13; s_sq=%5B
%5BB%5D%5D;s_vi=[CS]v1|2A841A13051D0B97-400001280000490C[CE]; tosAgreed=true"

那是不是看起来一团糟,完全正确。读取一个Cookie就意味着将字符串解析成多个由分号分隔的部分。另外还要注意,你没有访问任何元数据。通过document.cookie值无法获取这类信息。字符串解析并不是很难,但实际上,你不需要那样做。在本章的末尾,我会介绍一个优秀而小巧的库,它让你可以更轻松地使用Cookie。

删除Cookie

要删除一个Cookie,只需要将其过期时间设置成一个过去的时间:

document.cookie = "name=Raymond; expires=Thu, 01 Jan 1970 00:00:00 GMT";

从技术上讲,这个值无关紧要,但名称必须与你想要删除的Cookie一致。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注