最近更新了我们的 站,它是经过了设计上的全面验收的。但实际上,作为软件开发者,我们会注重很多技术相关的零碎的东西。我们的目标是控制性能,注重性能,未来可伸展,为 站增添内容是一种乐趣。接着就来告诉你,为什么我们的 站速度比你们的快吧(抱歉,确实是这样的)。
性能设计
在我们的项目中,我们每天都会和设计师和产品负责人讨论关于平衡美观和性能的问题。对于我们自己的 站,这样做是很简单的。简言之,我们认为好的用户体验从快速的内容传输开始,也就意味着 性能 > 美观。
好的内容、布局、图片和交互是吸引用户的重要因素。这每个因素都会影响页面的加载时间和终端用户体验。每一步我们都在探讨如何在获得好的用户体验和保证设计美感的同时,最小化对性能的影响。
内容优先
我们想要把核心内容尽快地呈现给用户,意味着我们要处理好基本的 HTML 和 CSS。每个页面都应该达到基本的目的:传递信息。JS、CSS、 页字体、图片、 站分析等优化都是位居于核心内容之下的。
可控性
给理想 站定义了标准后,我们总结出:要想达到预期效果,就要能对 站各方面的控制都游刃有余。我们选择构建自己的静态站点生成器,包括资源传输,并且由我们自己操控。
静态站点生成器
我们用 Node.js 实现了静态站点生成器。它是采用带有简短 JSON 页面描述标签的 Markdown 文件来生成整个 站结构和它所有的资源。为了包括特殊的页面脚本,也可以附带一个 HTML 文件。以下是一个简单化的描述标签和 markdown 文件,用于博客的发布,用它来生成真正的 HTML。
- JSON 描述标签:
- {
- “keywords”: [“performance”, “critical rendering path”, “static site”, “…”],
- “publishDate”: “2016-07-13”,
- “authors”: [“Declan”]
- }
markdown 文件:
- # Why our website is faster than yours
- We’ve recently updated our site. Yes, it has a complete…
- ## Design for performance
- In our projects we have daily discussions…
图片传输
平均一个 2406kb 的 页中 1535kb 是图片。就因为图片在 站中占据了这么大的一个比例,所以它也是性能优化的重点之一。
自定义 页字体
在深入之前,这里有一个关于在浏览器设置自定义字体的简短介绍。当浏览器发现CSS里面有@font-face 的定义,但是用户的电脑并不支持该字体时,它会尝试下载该字体文件。在下载时,多数浏览器根本不会用这种字体来展示文本。这种现象称为“不可见文本的闪现” 或者 FOIT。如果你有留意,你会发现 页上都有这种情况存在。如果你问我,我会告诉你这会影响用户体验。它延迟了用户读取他们所需内容的时间。我们可以迫使浏览器改变这种行为,变成 “无样式内容闪现” 或者称为 FOUT。我们告诉浏览器先使用普通字体,像 Arial 或者 Georgia。当自定义的字体下载完成后,再代替标准字体并且重新渲染。这样,即使自定义字体下载失败,仍然不会影响内容的可读性。然而,有人会认为这是一种妥协的做法,但我们认为自定义字体只是一种优化。尽管没有自定义字体, 页看起来也完好,也能百分百的正常运行。勾选/不勾选复选框来切换我们的 页字体,来自己体验一下:
切换下载的字体类
使用自定义 页字体可以改善我们的用户体验,只要你能够优化他们,并且负责任地为之服务。
字型子集设定
到目前为止,子集设定是改善 页字体性能最快的方式。我将会向每个使用自定义字体的 页开发者推荐它。如果你能完全控制 页内容,并且知道它将要展示哪些特性的话,你可以完全使用子集设定。但是,即使是仅仅把字体设为西方语言,也会对文件大小造成很大的影响。例如,我们的 Noto Regular WOFF 字体,默认是246KB,将其设为西方语言后,大小下降到31KB。我们使用 Font squirrel webfont, 这种字体真的很易用。
字体监听器
Bram Stein 推出的字体监听器是一个很了不起的脚本,可以帮助检查字体是否已被加载。至于你是如何加载字体的,是通过一个 页字体服务,还是自己上传就不可知了。在这个监听器告诉我们所有自定义的字体已经下载完毕后,我们就可以在 元素上添加一个字体加载完毕的类,我们的页面就会重新用新的字体:
- html {
- Georgia, serif;
- }
- html.fonts-loaded {
- Noto, Georgia, serif;
- }
注意: 为了简短,我没有给上面CSS中的 Noto 加上 @font-face 的声明。
我们可以设定一个cookie来记住所有的字体已经被加载过,就可以让他们缓存在浏览器里面了。我们使用这个cookie来做重复的浏览,这个我后续会解释。
在不久的将来,我们或许不需要 Bram Stein 的脚本来监听这个行为。CSS开发团队已经提案一个新的 @font-face 描述器,也叫font-display。它的属性值控制着一个可下载的字体是如何在还没加载出来时就渲染页面的。这是CSS对font-display的描述:它将带给我们像上面方法一样的行为效果。你可以读读更多关于 font-display 的属性。
JS和CSS懒加载
一般来讲,我们都是尽可能快的加载需要的资源。我们移除一些堵塞的请求来加快页面渲染,优化首屏,用浏览器缓存来处理重复的页面。
JS懒加载
设计上,我们的 站并没有很多JS。我们发展了一个JavaScript工作流来处理我们目前已有的js, 以及未来会用到的js资源。
JS在
块里面渲染,这是我们想要的。JS应该只是用来提高用户体验,不应该是访问者需要的关键。处理JS堵塞渲染的简单方法就是把脚本放在页面的尾部。这样 页就会在整个HTML 渲染完毕后才去加载JS。另一种可以把脚本放在 head 执行的方案是在
我们把这小段脚本放在页面头部,来检测浏览器是否支持原生JavaScript的 document.querySelector 和 window.addEventListener属性。如果支持,我们通过
Apache 服务端逻辑看起来像一行一行的备注,一般以 如果是true的话,我们就假定这是用户的第一次浏览。
第一次浏览我们添加了