最近,我手上的石膏被拿掉了,于是我下楼去帮室友。楼下室友领导是阿里的,前端采用阿里的蚂蚁设计。设计上传到蓝色泻湖,聊天工具也用了阿里的指甲。有时间的时候也下楼和室友交流学习。如果我有更多的交流,我会被提升。我最近一直很忙。好久没更新博客了。我们不要说太多。今天我们主要讲的是前端性能优化的浏览器缓存。
我之前写过浏览器缓存和html5离线缓存。我们很容易对这些缓存感到困惑,比如200 OK(来自内存缓存、来自磁盘缓存)、304 NOT MODIFIED和应用缓存等。
来自内存缓存的200不访问服务器,而是直接读取缓存并从内存读取缓存。此时,数据将缓存在内存中。击杀过程结束后,即浏览器关闭后,数据将不存在。但是这个方法只能缓存派生资源。
来自磁盘缓存的200不会访问服务器,而是直接读取缓存并从磁盘读取缓存。当终止过程发生时,数据仍然存在。只有派生资源才能以这种方式缓存。
04 notmodified访问了服务器,发现数据没有更新。服务器返回了此状态代码。然后从缓存中读取数据。
先检查内存,如果有,直接加载。
如果没有内存,选择硬盘获取,如果有直接加载。
如果没有硬盘,则进行网络请求。
加载的资源缓存到硬盘和内存中。
一般浏览图片,以下流程:
访问-200-退出浏览器
然后进入-200(来自磁盘缓存)-刷新-200(来自内存缓存)。
应用缓存和上面的缓存有点不一样,是离线缓存,也就是不用联网就可以从硬盘读取资源,即使网络断开,用户也可以浏览。
304无论是协商缓存还是与服务器通信一次,如果要切断服务器通信,必须强制浏览器使用本地缓存(cache-control/expires)。
一般来说,设置浏览器缓存有几种方法。
1.通过HTTP的META设置过期和缓存控制。
http-e
quiv="Cache-Control" content="max-age=7200" /> http-equiv="Expires" content="Sun Oct 15 2017 20:39:53 GMT+0800 (CST)" />这样写的话仅对该网页有效,对网页中的图片或其他请求无效。
2、apache服务器配置图片,css,js,flash的缓存
这个主要通过服务器的配置来实现这个技术,如果使用apache服务器的话,可以使用mod_expires模块来实现:
编译mod_expires模块:
Cd /root/httpd-2.2.3/modules/metadata/usr/local/apache/bin/apxs -i -a -c mod_expires.c //编译
先打开httpd.conf文件,然后查找expires这个模块,找到后,删除左边的#号,表示打这个模块,并重启apache服务器
编辑httpd.conf配置:添加下面内容
mod_expires.c>ExpiresActive onExpiresDefault "access plus 1 month"ExpiresByType text/html "access plus 1 months"ExpiresByType text/css "access plus 1 months"ExpiresByType image/gif "access plus 1 months"ExpiresByType image/jpeg "access plus 1 months"ExpiresByType image/jpg "access plus 1 months"ExpiresByType image/png "access plus 1 months"EXpiresByType application/x-shockwave-flash "access plus 1 months"EXpiresByType application/x-javascript "access plus 1 months"#ExpiresByType video/x-flv "access plus 1 months"
3、php等设置
php header("Cache-Control: public"); header("Pragma: cache"); $offset = 30*60*60*24; // cache 1 month $ExpStr = "Expires: ".gmdate("D, d M Y H:i:s", time() + $offset)." GMT"; header($ExpStr);?>
或者
$seconds_to_cache = 3600;$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";header("Expires: $ts"); header("Pragma: cache");header("Cache-Control: max-age=$seconds_to_cache");
问题一:有了缓存,如何进行前端代码更新呢?
我们可以在资源文件或者图片后面添加版本号,如下图。
问题二:但是所有文件都加了版本号之后,我们只更改了一个文件,其他文件的缓存不是浪费了吗?
解决这个问题,我们可以用数据摘要要算法,对文件求摘要信息,摘要信息与文件内容一一对应。如下图:
这样就解决了这个问题。
问题三:新的问题又来了,文件发布怎么办?
1、先部署页面,再部署资源:在二者部署的时间间隔内,如果有用户访问页面,就会在新的页面结构中加载旧的资源,并且把这个旧版本的资源当做新版本缓存起来,其结果就是:用户访问到了一个样式错乱的页面,除非手动刷新,否则在资源缓存过期之前,页面会一直执行错误。
2、先部署资源,再部署页面:在部署时间间隔之内,有旧版本资源本地缓存的用户访问网站,由于请求的页面是旧版本的,资源引用没有改变,浏览器将直接使用本地缓存,这种情况下页面展现正常;但没有本地缓存或者缓存过期的用户访问网站,就会出现旧版本页面加载新版本资源的情况,导致页面执行错误,但当页面完成部署,这部分用户再次访问页面又会恢复正常了。
好的,上面一坨分析想说的就是:先部署谁都不成!都会导致部署过程中发生页面错乱的问题。所以,访问量不大的项目,可以让研发同学苦逼一把,等到半夜偷偷上线,先上静态资源,再部署页面,看起来问题少一些。
如何解决这些问题呢?
这个问题,起源于资源的 覆盖式发布,用 待发布资源 覆盖 已发布资源,就有这种问题。解决它也好办,就是实现 非覆盖式发布,如下图:
看上图,用文件的摘要信息来对资源文件进行重命名,把摘要信息放到资源文件发布路径中,这样,内容有修改的资源就变成了一个新的文件发布到线上,不会覆盖已有的资源文件。上线过程中,先全量部署静态资源,再灰度部署页面,整个问题就比较完美的解决了。
1、 配置超长时间的本地缓存 —— 节省带宽,提高性能
2、采用内容摘要作为缓存更新依据 —— 精确的缓存控制
3、 静态资源CDN部署 —— 优化网络请求
4、更资源发布路径实现非覆盖式发布 —— 平滑升级