上一篇文章主要聊了如何减小一个页面的体积,这个话题还没聊完,继续。

不管作为网站运营方也好,网站访客也好,都很难确定一个数值作为标准,来判断网页体积减少到多少才算合理。尽管如此,总有可努力的方向让网站的表现更好。比如,从访客体验来讲,一个页面完全加载出来需要20秒,这肯定无法容忍。1秒?感觉挺快。2秒?感觉没区别。3秒?怎么网页卡了?哦,刷出来了,感觉还行。5秒?感觉有点慢。6秒?好像和5秒没什么差别……不管怎样,总有这么一个区间,会让访客进入“焦躁”状态。

也正因为没有一个明确标准,所以在这件事上,只能说尽力而为,适可而止,差不多就行。

一个页面承载过多的内容,是否有必要?

页面内容过多的坏处:

  1. 我这种面向搜索引擎开发的运维,动不动就开十几个页面,一个页面渲染慢点还能接受,大家商量好了一块慢,我这活就没法干了;
  2. 一个页面请求过多内容,势必消耗大量带宽,如果后端请求也多,还会拖累服务器性能,这种时候,要么打死产品,要么自己想办法解决。

那怎么才算内容多?

“多”分两种,一个是体积大,一个是资源数量多。

一个页面10MB,不管用什么标准来衡量,都太大了,正如开头所说的加载时间长短标准一样,虽然知道一个页面10MB大,但又没有一个明确的标准来衡量到底多大才不算大,只能以经验数据来进行把控。从成本角度出发,这个数值应该以服务器承受能力来定,硬标准是:总带宽 / ( 8 * 在线用户数量),得到的是每个用户可用的平均带宽。这只是一个需要严防死守的底限数据,实际应当做到比这个数值要小。个人建议尽量将普通的页面压到1MB以内,图片详情页面可适当放宽标准,那如果实在压无可压怎么办?

回头再看下浏览器工作流程。

浏览器请求到一个页面时,首先下载HTML源码,然后根据源码进行渲染,如果碰到额外的资源引用,比如js、css,还需要下载这些引用资源。所以打开一个页面往往不止一个下载动作,下图中就有43个请求。

HTTP协议属于应用层协议,基于TCP协议来实现,而TCP特性之一就是面向连接:要通信,先建立连接,用完拆除。当页面中有43个请求的时候,意味着要进行43次TCP通信,资源数量越庞大,TCP连接建立和拆除的次数就越多,即时传输相同体积的数据,耗时也会更多。针对这个问题进行优化的方法是,减少资源数量,比如合并css、js。

再回过来看看瀑布图:

浏览器本身存在一个并发限制,即同一时间向同一服务器发起的请求数量存在上限(不同浏览器该数字也不一样,4-8不等),这个限制也导致了资源数量过多时,页面加载速度变慢。优化方法除了上面所说的减少资源数量外,还可以分散部分资源到其它站点上,比如为图片专门建立服务器,使用二级域名img来发布,这样客户端既能更快速地下载内容,服务器端又通过多个服务对压力进行分散负载。

除此之外,还有一个技术也能实现压力分流的目的,甚至可以说,这项技术的存在就是为了这个目的:CDN(免费CDN推荐使用cloudflare)。CDN的主要功能有两个:降压、提速(只限于对较差的网络环境进行改善)。将服务接入CDN后,访客不再直接向服务器请求资源,而是转向CDN。上,向服务器请求真实资源进行缓存;下,为终端用户提供基于地理位置或网络优化过的缓存内容。不过其使用场景有严格的限制,单向资源请求类的资源可以使用(比如下载、点播、直播之类的需求),双向资源请求类的服务场景就不可以使用了(比如视频聊天、游戏)。

测测看:

  • 页面还是用 https://www.chenxin.info/2020/06/20/publish-printers-via-cupsd-service/
  • 这个页面的加载内容里,体积最大的资源是一个播放器,js+css居然有600K,删掉(通过移除资源实现数量和体积的优化);
  • wordpress有一个插件叫Autoptimize,可以对js,css,html内容进行压缩,装上以后把能打开的全打开(通过合并资源、删除冗余内容实现数量和体积优化)。

刷新页面看下结果:

经过大量的js和css合并,请求数量减少到11个,并且移除了播放器以后,传输带宽消耗直接降到183KB,资源总体积降到572KB,不过不太对,怎么加载时间变成5.53秒了?

原因在于我为这个网站开启了CDN。因为网站部署在阿里云,响应速度其实已经很快了,访问请求经过CDN服务转发,反而加长了响应时间,如果只是为了追求测试结果,可以把CDN移除(免费CDN的性能表现不佳,这个问题会表现得更明显)。

虽然看上去测试结果的加载时间更长了,但在大压力场景下CDN仍然是不可或缺的。由于服务器高昂的带宽成本,硬扛全部流量压力即使能做到,也会是一项不切实际的方案,在不加带宽的前提下,只有借助CDN才能将成本控制在可以接受的范围,甚至是变不可能为可能,所以这个场景里它仍然是一个必选项。

不过这也意味着,服务器带宽足够的情况下,用CDN“有可能”会带来反效果,之所以说“有可能”,是因为在网络状况差的情况下,它确实能起到提速效果,只是在当前测试环境里或带宽和网络状况未成为响应速度的制约因素时,CDN起的是反效果。

把这个网站的CDN移除掉看一下新的结果:

通过图片可以看到,加载完成只需1.5秒,相比只压缩了图片的最初版,耗时减少了60%。

页面大小一定不能超过1M吗?

倒也未必,很多网站都喜欢用瀑布流布局,而且以图片居多,这种页面的体积肯定小不了。但是它并非一次性把所有资源都加载进来,而是使用懒加载的方式:先加载一屏,给访客慢慢看,后面的反正还没看到,就索性不加载,等要看了再加载一屏。通过这种方式,将整个页面的请求分割为多个批次,按需下载,虽然页面体积很大,但每次请求的内容却很少。

同样的思路也可以用在页面设计中:如果加载慢,那就先显示出来一点什么东西,告诉访客页面正在加载,总比显示一个空白页面强。这就好比去吃饭,第一个上的菜永远是凉菜,哪怕你没点凉菜,送也要送一盘花生米,上菜越慢的馆子越喜欢来这套。

页面加载卡顿了,怎么办?

因为之前提到过,浏览器在请求内容时,与同一服务器建立的连接是有数量限制的,当某个资源下载时出现问题(比如资源过大、无法访问、响应超时、内容错误)时,其它资源下载可用的连接数将减少1,出问题的资源越多,可用连接数将越少。

另外,虽然浏览器可以同时下载多个资源,但是总体加载流程其实是在分批次的基础上线性进行的一个过程。中间一个环节出现问题导致卡住,不仅占用一个可用连接,还会导致后续有依赖关系的资源一直处于等待下载的状态。

非常典型的一个问题就是Google Fonts(现在已经可在部分地区直接访问),一旦Google Fonts的请求被阻断,优化不好的网站会直接卡在加载阶段直到超时,最终网站显示也会出一堆问题,所以才衍生出了一系列替代方案(反向代理、直接下载字体文件、公共CDN等)。

想解决上面的问题,就需要对资源进行优化,替换掉速度慢甚至无法访问的资源,以实现稳定性需求。

此外,前端在页面制作时,出于优化加载流程的目的,也会视情况将js放到页面末尾,这样的话,即使js出了问题,也不会影响到页面渲染;同时,为了不因为js执行导致的阻塞中断页面加载的过程,还会使用异步加载的方式来引入js。

说了一堆“请求下载”的问题,稍微总结一下:

  • 开启gzip压缩;
  • 合并js、css、html资源,减少资源数量,挤压冗余内容;
  • 使用框架/插件时选择压缩版,并且避免额外引入不需要的资源;
  • 在可接受范围内,通过牺牲清晰度压缩图片;
  • 尽量缩减网页内容,内容较多里使用懒加载方式请求资源;
  • 优化引用资源,选用更稳定的源;
  • 尽可能将js放到末尾,不阻断页面渲染;
  • 多站点分拆业务,绕开同服务器下载的并发限制;
  • 一时半会加载不出来,随便弄点进度显示(哪怕没意义的动态效果)给访客先看着;
  • 异步加载;
  • CDN有条件就要上,但它不是万能灵药,并非所有场景都适用。
分类: articles