如果说2012年对于web开发世界来说有什么值得记住的事的话,关于localStorage性能的争论一定高居榜首。这场争论开始于Christian Heilmann写的一篇文章: There is no simple solution for localStorage(中文版:本地存储并不简单)。在这篇文章里,他得出了几个关于localStorage性能差的几个论断。除此之外,他还建议对现有api进行改变以及对于可选api(IndexedDB、webSQL)的优化。
但是这篇文章的读者并不多,只有很少的文章进行深入分析。John Allsopp写了一篇叫localStorage perhaps not so harmful的文章,在这篇文章里他分析了通过localStorage读写10KB的数据的时间。我也写了一篇文章来说明我的观点: localStorage无罪!(In defense of localStorage)。在这篇文章里我比较了相同条件下的localStorage和cookie(和磁盘访问读写的性能差不多)的读写性能。我和John Allsopp根据分析得出的结论都认为localStorage的性能问题并没有严重到抛弃使用它的地步。
在和Mozilla的Taras Glek(在他自己的博客上写了一篇相关主题的文章:PSA: DOM localStorage considered harmful)交流之后,我意识到自己和John Allsopp的测试方法和localStorage真正的运行方式相比是有缺陷的。
localStorage的关键问题在于它是通过同步操作的方式来进行文件I/o操作。写入localStorage的数据都会保存到磁盘上,除非主动删除数据,否则数据是永远不会过期的。用过nodeJs的人都知道,对于文件的I/O是非常昂贵和不一致的(不可信赖)。任何时间点任何的程序都可以访问文件。举例来说,你注意到过当一个杀毒软件运行的时候你的电脑是如何慢下来的吗?在理想状态下,你读取的文件不会有其他程序在同一时间访问该文件。在极端坏的情况下,如果你想读取一个文件,就必须等待文件上的锁被释放(其他程序操作文件时会锁定文件)。
这就引申出一个浏览器的问题:到底什么时候磁盘的数据才应该被读取?只有两种可能。第一种,数据可以在页面加载时就被读取,这样可以确保后面的读取快速操作。当然,这也意味着localStorage将会影响页面的加载时间,即使localStorage读取的数据并不会使用。在理想情况下,你并不会注意到这有多大不同。但在极端坏的情况下,这可能会导致页面加载时间的延长。
第二种方式是在localStorage第一次被使用(JS操作)的时候再从磁盘读取数据。这样可以阻止对于页面加载的中断,但也意味着在第一次通过localStorage访问数据的时候浏览器会中断对于页面的处理(js执行、页面渲染等)。磁盘文件的所有localStorage数据都会被写进内存以加快后面对于localStorage数据的读取速度。同样地,通过localStorage保存的数据会首先写入内存,以后再写入到磁盘文件里,以加快写的速度。firefox和chrome都使用了第二种方式(opera好像也是如此,我没有验证过)。这也导致了通过像jsPerf这样从不加载页面的工具来测量localStorage的性能是很难保证准确度的。
现在问题已经很清楚了:第一次通过localStorage.getItem()读取数据的时间是不可预测的。此外,这是一个阻塞型方法,因此浏览器会停止处理页面直到数据从磁盘中读出。后面我和John Allsopp的一起合作的测试(第一次慢读取,后面的快读取)表明localStorage也不是那么坏。最近,Chromium的工程师William Chan做了一些通过测量第一次读取时间来判断localStorage性能的分析。
William的分析结果表明在Windows, Mac, 和Linux上75百分位数(就是说75%的测验都是很快的,只有25%有点慢)的测验读取速度都很快。事实上,在Windows和Linux上,只有99百分位数(99%的测试的首次读取时间小于1s,只有1%的测试超过1s)的测试的首次读取时间超过一秒(Mac上仍旧小于1s)。如下图表所示:
注:百分位数(percentile)的概念参考百度百科http://baike.baidu.com/view/1323573.htm
William Chan’s localStorage Read Performance Numbers
Taras Glek在Google+ comment上说他也分析出firefox也有类似的性能特征。Taras Glek和William的结论都证明了localStorage的性能并不像我们一开始想的那样低下。
尽管如此,我还是自己做了一些试验。通过在高级浏览器下使用performance.now(),在不支持时使用Date对象的方式来测量localStorage的首次读取时间和后面的读取时间。下面是我的一些发现(除非特别声明,测试环境都是Windows 7):
在上面的结论中有个有趣的现象,safari好像会把所有的数据都保存在内存中,直到应用程序关闭才再次读取磁盘。只要safari保持运行,数据读取将继续只花费0ms。
基于以上结果,我没有看到在桌面电脑上不使用localStorage的任何理由。虽然99百分位数有些差的数据,但这仅仅是一些极端值。从平均数来看,localStorage的性能在所有浏览器下还是表现的很好的,即使是第一次读取的时候。
在移动设备下还需要更多更深入的研究。在移动设备上对磁盘进行操作比桌面设备更加昂贵,我们应该尽量减少对于磁盘的操作。不过,至少在IOS下这种花费通过只有在每个应用程序session中才读取一次的方式减轻了很多。由于大多数情况下用户并不会关闭safari,因此我们应该判断是否有必要花费初始化读取数据的时间。如果24ms比从服务器读取相同资源的时间要少,那么通过localStorage存储数据的性价比就会很高。
总的来说,我还是觉得对于localSorage性能差的论断有点草率。最新的数据表明localStorage的性能并不像早期有些文章写的那样让人不敢使用这项技术。磁盘I/O操作虽然一般情况下比较慢,但这是一个很好的例子表明越多的人关注和亲身测试对于一些有争论的观点对人们错误的引导有很大的抑制作用(事实胜于雄辩)。有很多网站都在使用这个api,但并没有哪家网站公开宣称localStorage存在性能问题。没有了性能上的问题,虽然在页面加载时使用localStorage需要思量再三,但我还是会尽可能的使用localStorage。
补充:在windows 8的IE10下,默认localStorage是不可访问的。如果我们直接使用localStorage的话,控制台会报Error:拒绝访问。解决方式如下:Get the Internet Explorer script error which says "Access denied error for LocalStorage"