在上一节性能优化之静态资源优化中,我们对博客中使用到的 CSS、JavaScript、图片等做了比如大小、合并、懒加载等优化,在这一节中,我们将使用浏览器的缓存能力,对这些资源进行进一步的优化,来提升首屏加载速度等,并且我们还将讨论,针对缓存在浏览器中的数据,如何进行缓存的更新。
使用 LocalStorage 进行本地存储
关于 LocalStorage 的介绍可以查看 Window.localStorage | MDN 和 使用 Web Storage API,我这里就不再赘述了。
首先,我们需要进行本地存储的项目有,CSS 文件和 JS 文件,特别是 CSS 文件中添加了 Data URI 以后,文件的大小会增加很多,这种文件可以考虑使用 LocalStorage 进行缓存,也有一些全局的样式,全局的 JS 文件,或者一些不经常改变的文件或样式都可以考虑使用 LocalStorage 进行缓存,下面,我们将展示的是对 CSS 文件的缓存。
函数的定义
首先定义 LocalStorage Save(ls
) 函数和 LocalStorage Load(ll
) 函数,用来保存文件内容到 LocalStorage 里面和从 LocalStorage 读取内容,添加到 <head>
中。
1 | function ls(name) { |
除了实现添加功能以外,还需要使用 cookie
来标记用户。如果用户已经下载 common_css
,则下次就不用在 HTML 页面中添加 common_css
的部分,而是转为调用 ll('common_css', 0);
。这一部分的内容通过 Express 中间件来实现:
1 | exports.checkll = function(req, res, next) { |
将 visited
获取后传给 Pug 来设置是使用 ls('common_css')
函数还是使用 ll('common_css', 0);
。
1 | block commonStyle |
修改 Pug
模板中的内容,将需要缓存的公用的内容再重新分离出来,比如 header
,page-nav
等内容。然后把不同页面转换的时候要切换的文件写在一个新的 <style>
标签中。
1 | block commonStyle |
最后,我们可以比较一下使用 LocalStorage 前后传输的文件的大小差距:
我们测试的网络环境是 Good 3G (40ms, 1.5Mb/s, 750kb/s)
当没有使用 LocalStorage 缓存的时候:
我们可以看到,文档的大小是 43.4KB,其中有很大一部分是 CSS 的大小,因为我们把 header 的背景图片使用 Base64 编码添加到了样式中。当我们下次请求本页面或者其他页面的时候,添加了图片的样式已经在 LocalStorage 缓存:
我们可以看到,此时请求的文档大小只有 5.4KB,节省了网络中传输的资源。
本地存储的更新
有一些不经常修改,并且比较大的文件,我们可以使用 LocalStorage 来进行本地存储,但是不经常修改并不是代表以后都不会修改,那我们如何确定本地存储的文件是最新的版本的文件呢?我们又应该如何对本地存储的文件进行更新呢?
下面我们将针对几种本地缓存的协商策略进行对比,来选择一个合适的策略,使用在我们的博客系统上。
我的博客使用的是 Express 作为后台,Express 默认对静态的资源开启了 Etag
、Last-Modified
和 max-age
来支持浏览器缓存,如果我们想实现自己的缓存策略,可以选择关闭已有的缓存策略,然后自定义自己的缓存策略。
比如说,我们在开启了 Etag
之后,每一次浏览器打开该页面,还是会向服务器询问一次该文件是否更新,然后服务器返回 304
回应来声明资源没有被修改,浏览器向服务器进行请求的时候就会消耗一些交互资源。
所以,在设置更新的时候,我选择离开如下的设置方式:
1 | Cache-Control: public, max-age=31536000 |
使用如上的方式,可以在过期时间内不发送请求而是直接使用缓存,又能保证在资源有更新的,只要通过给资源增加时间戳或者更换路径,就能让用户访问最新的资源
1 | app.use(express.static(path.join(__dirname, './www/static'), { |
针对非私密性和经常性变动的资源,比如我们的 CSS 文件需要经常调整,所以我们不能设置 max-age
和 expires
缓存:
1 | Cache-Control: public, max-age=0 |
除此以外,我们还需要对存在 LocalStorage 中的数据进行更新。更新的方法是,在我们原来定义 ls
和 ll
函数的时候,需要添加一个标签定义该文件的版本号,并且将该版本号存在 cookie 中,当浏览器请求数据的时候,如果我们从 cookie 中解析得到的版本号不是最新的,则该资源还需要重新下载。所以,我们将修改 ls
函数:
1 | function ls(name, tag) { |
也就是,我们在缓存文件的到 LocalStorage 的时候,需要赋值一个 v
参数来计算该文件的版本。在我的博客中,针对 common_css
这个文件,我们使用 MD5 算法在运行 Express 的时候自动生成一个摘要作为 v
的值,如果文件发生了变化,则该 v
的值会自动根据文件内容生成。