一西一东

10倍性能提升:优化一个静态网站

原文链接: hackernoon.com

几个月前,我在国外旅行,当时想给一个朋友展示我的个人(静态)网站。我尝试导航到我的网站,但花费的时间比我预想的要长得多。这个网站基本没有动态的内容--它是有些动画和响应式设计,但内容一样。我震惊于这样的结果:6秒才完成DOMContentLoaded,8.2秒才整页下载完成。这个静态网站有50个请求,总数据大小2.9mb。我习惯了网速1GB/s,只需从洛杉矶连接到旧金山的服务器,这使得这个怪物般的网站看起来非常快。在意大利,在每秒8mb的网速下,情况完全不同。

这是我第一次研究网站优化。以前,我要添加一个库或者资源,就把数据放进去,并通过src=""指向资源。我完全没有关心性能,不管是缓存或者懒加载。

我开始寻找有相似经验的朋友。不幸的是,很多有关静态优化的文献早已过时--有些是来自2010或者2011年讨论的内容,有些前提假设已经错误,有些把那些无用的话重复了一遍又一遍。

然而,我还是发现两处优秀的资源-- High Performance Browser NetworkingDan Luu’s similar experience with optimizing static sites。虽然我没有像Dan一样去除格式和内容,我还是让我的页面加载速度快了10倍。达到了大约五分之一秒就完成DOMContentLoaded和355ms下载完整个页面。

进程

第一步是对网站进行整体研究。我想要弄清楚什么花费了最多的时间,应该怎么并行请求。我使用好几种工具,从世界各处测试了我的网站,包括:

有些工具是能给出有提升作用的意见,但只有当您的静态网站只有50个请求时才能做到这一点--就像所有东西都来自一个空gif图像,这种从90s年代遗留下来却从未被使用的资源(我下载了6个字体但只用了一个)。

这是我网站的时间线--我在Web Archive上测试了。虽然我没有提供最原始的数据,但这个数据很像我几个月前看到的原始数据了。

所有我能控制的一切我想要提高--从内容和通过javascript连接服务器(Nginx)的速度和DNS设置。

优化

缩小和压缩内容

首先我注意到的是,不管是CSS还是JS,我都有十多个请求。这些JS请求都没有用HTTP keep-alive形式;对于不同网站,有的还是https请求。这添加了多条路线去到不同的CDN或者服务器,而且有些js文件还请求了其它资源,这就形成了上面看到阻塞联级。

我使用webpack 去合并我的资源使其变成单一的文件。任何时候,只要内容改变,它就会自动压缩,并且让所有依赖的资源变成单一文件。

我尝试了几种方案。原先这个单一的bundle.js文件放在<head>头部,这会形成阻塞。它最终的大小是829kb,包括了每一个非图片资源(字体,css,所有库,依赖文件和js文件)。这里面最大的是font-awesome字体,在829kb中占了724kb。

我将font awesome的字体和样式进行了处理,只留下我使用的3个字体-- fa-github, fa-envelope, and fa-code。我用 fontello ,它能让我用我需要的字体。结果文件的大小下降到94kb。

目前网站如果只用样式表去构建,就会出错。添加一个具有阻塞性质的bundle.js文件是必要的,加载时间为118ms,比上面的要好一个数量级以上。

还有一些额外的好处--我不再有资源指向第三方资源或者CDN,所以用户不需要:1)发送CDN请求;2)形成htpps握手;3)等待所有资源从第三方下载完成。

尽管CDN和分布式缓存对于大型网站,分布式网站是有用,但它对我这个小的静态网站没什么用。额外的几百毫秒损失是可以接受的。

压缩资源

我加载了一个8MB大小的头部图片,然后以10%的宽度/高度显示它。这不是缺少优化,而是对用户bandwidth的浪费。

我所有图片用 https://webspeedtest.cloudinary.com/ 压缩--它也提醒我转成用 webp,但是我想兼容更多的网站,所以我坚持使用jpg图片。也能添加一个机制,只让那些可以支持的网站使用,但我想保持简单,添加这种抽象层的代码似乎不值得。

提高Web服务-- HTTP2, TLS还有更多

我首先做的是变成https--原先我在80端口上运行Nginx,只是从 /var/www/html加载服务文件。

我开始设置成https,并且将我的资源请求都从http重定向到https。我从 Let’s Encrypt(一个伟大的网站,也可注册 wildcard certificates)获得我的TLS证书。

只需添加http2指令,Nginx就能够利用所有最新HTTP优势特性。注意,如果你想使用http2(前版本是SPDY)特性,你必须使用HTTPS。了解更多 here

你还可以使用_http2_push命令作用于图片images/Headshot.jpg;。

注意:启用gzip和TLS可能会使您面临BREACH的风险。基于这是一个静态网站,面临BREACH的风险非常低,压缩对我来说就不是问题。

利用缓存和压缩

还有什么是Nginx就可以完成的?第一个蹦出来的想法是缓存和压缩

我发送的是原始没有压缩的HTML的文件。只要单单添加 _gzip和_line命令,文件的大小从16000bytes变成8000bytes,下降了百分之五十。

我们还可以进一步提高速度,如果我们设置了 _gzip_static,它会去寻找以前压缩的版本。当与我们上面的webpack配置结合在一起--我们能在编译时使用ZopflicPlugin去预压缩我们所有的文件。这样就节省了计算资源,能让我们不用考虑速度去最大限度压缩我们的资源。

不仅如此,我的网站很少发生变化,所以我希望资源尽可能长时间被缓存。这样的话,在以后的访问中,用户就不需要重新下载所有内容(特别是 bundle.js)。

我更新完的服务器备注是这样的。注意,我并没有讲解所有我做的改变,比如TCP设置的变化,gzip指令和文件缓存。如果你想了解更多这些内容,read this article on tuning Nginx.

相应的服务器内容

懒加载

最后我网站还有一个小变化,速度会有稍许提升。有5张图片是看不见的,除非你按下相关的标签,但这些图片还是跟其他资源同时下载(因为这个标签)

我写了一个简短的脚本去修改那些拥有lazyload名称的元素的属性。

所以一旦内容完成加载完成,标签就会变成,这样才加载这些图片。

总结

这些使我页面第一次加载从超过8秒到小于350毫米,非第一次加载更是达到200毫秒。我强烈推荐阅读High Performance Browser Networking --它能很容易读完,却提供了现代网站的概况,对现在网络模型的每一层都给出了很好优化建议。

我有哪些遗漏了吗?是否一些方法违反了最佳实践或者有更好的提高性能的方法?请联系_JonLuca De Caro_!_