澳门新浦京娱乐场网站-www.146.net-新浦京娱乐场官网
做最好的网站

澳门新浦京娱乐场网站:从案例剖析哪些优化前

从案例分析如何优化前端性能

2016/08/30 · 基础技术 · 性能

原文出处: css-tricks   译文出处:王下邀月熊   

在 De Voorhoede工作的日子里,我们一直在追寻为用户构建高性能的前端解决方案。不过并不是每个客户会乐于遵循我们的性能指南,以至于我们必须一遍又一遍地跟他们解释那些保证他们能够战胜竞争对手的性能策略的重要性。最近我们也重构了自己的官方主页,使其能够拥有更快地响应速度与更好地性能表现。
澳门新浦京娱乐场网站 1

如何运用最新的技术提升网页速度和性能

2016/08/29 · 基础技术 · 性能

原文出处: Declan   译文出处:众成翻译   

最近更新了我们的网站,它是经过了设计上的全面验收的。但实际上,作为软件开发者,我们会注重很多技术相关的零碎的东西。我们的目标是控制性能,注重性能,未来可伸展,为网站增添内容是一种乐趣。接着就来告诉你,为什么我们的网站速度比你们的快吧(抱歉,确实是这样的)。

前言
本文是@旭日云中竹带来的翻译与投稿。文章中提到的图片格式webp,自定义网页字体,懒加载等技术的运用,这些在大家的项目中现在运用的如何了呢?

作者:Declan
译者:旭日云中竹
链接:http://www.zcfy.cc/article/1030
原文:https://www.voorhoede.nl/en/blog/why-our-website-is-faster-than-yours/

本文提到的网站性能指网站的响应速度,这也符合绝大部分人对于网站性能的理解:访问快速的网站性能好,反之,访问速度越慢,则网站性能越差。本文总结的优化方法是宏观的工程层面的方法,并不包含微观的语言语法层面的方法,例如,JS、CSS的语法优化,这一部分同样影响网站的性能,但语言语法层面的优化更多的是取决于开发人员的编程水平。

性能调优始于设计

在前端项目中,我们常常与产品经理以及UI设计讨论如何在美感与性能之间达到平衡,我们坚信更快地内容呈现是好的用户体验的不可分割的一部分。在我们自己的网站中,我们是以性能优于美感。好的内容、布局、图片与交互都是构成你网站吸引力的不可或缺的部分,不过这些复杂的元素的使用往往也意味着页面加载速度的增加。设计的核心即在于决定我们网站需要呈现哪些内容,往往这里的内容会指图片、字体这样的偏静态的部分,我们首先也从对于静态内容的优化开始。

性能设计

在我们的项目中,我们每天都会和设计师和产品负责人讨论关于平衡美观和性能的问题。对于我们自己的网站,这样做是很简单的。简言之,我们认为好的用户体验从快速的内容传输开始,也就意味着 性能 > 美观

好的内容、布局、图片和交互是吸引用户的重要因素。这每个因素都会影响页面的加载时间和终端用户体验。每一步我们都在探讨如何在获得好的用户体验和保证设计美感的同时,最小化对性能的影响。

正文
最近也更新了我自己的网站,它是经过了设计上的全面验收的。但实际上,作为软件开发者,我们回注重很多技术相关的零碎的东西。我们的目标是控制性能,注重性能,未来可伸展,为网站增添内容是一种乐趣。接着就来告诉你,为什么我们的网站速度比你们的快吧。

最近更新了我们的网站,它是经过了设计上的全面验收的。但实际上,作为软件开发者,我们会注重很多技术相关的零碎的东西。我们的目标是控制性能,注重性能,未来可伸展,为网站增添内容是一种乐趣。接着就来告诉你,为什么我们的网站速度比你们的快吧(抱歉,确实是这样的)。

什么样的网站响应速度快呢?其实很容易想到,网站加载资源的速度越快,网站响应速度越快;网站需要加载的资源越少,网站响应速度越快。这就分别对应网站性能优化的两大方向:资源缓存、资源合并压缩。当浏览器完成资源的加载后,需要进一步解析资源,才能渲染出最终的网页,所以,浏览器的解析机制也是网站性能优化的一个方向。各种优化方法都可以归类到这三个大方向中。

Static Site Generator

为了演示与测试方便,我们基于NodeJS搭建了一个混合使用MarkDown与JSON作为配置的静态网站生成器,其中一个简单的博客类型的网站的配置信息如下:

JavaScript

{ "keywords": ["performance", "critical rendering path", "static site", "..."], "publishDate": "2016-08-12", "authors": ["Declan"] }

1
2
3
4
5
{
  "keywords": ["performance", "critical rendering path", "static site", "..."],
  "publishDate": "2016-08-12",
  "authors": ["Declan"]
}

而其内容为:

JavaScript

# A case study on boosting front-end performance At [De Voorhoede]() we try to boost front-end performance... ## Design for performance In our projects we have daily discussions...

1
2
3
4
# A case study on boosting front-end performance
At [De Voorhoede](https://www.voorhoede.nl/en/) we try to boost front-end performance...
## Design for performance
In our projects we have daily discussions...

下面,我们就这个静态网站,进行一些讨论。

内容优先

我们想要把核心内容尽快地呈现给用户,意味着我们要处理好基本的 HTML 和 CSS。每个页面都应该达到基本的目的:传递信息。JS、CSS、网页字体、图片、网站分析等优化都是位居于核心内容之下的。

性能设计

在我们的项目中,我们每天都会与设计师和产品负责任讨论关于平衡美观与性能的问题。对于我们自己的网站,这样做是很简单的。简言之,我们认为好的用户体验从快速的内容传输开始,也就意味着 性能 > 美观。

好的内容、布局、图片和交互式吸引用户的重要因素。这没个因素都会影响页面的加载时间和终端用户体验,每一步我们都在探讨如何在获得好的用户体验和保证设计美感的同时,最小化对性能的影响。

性能设计

在我们的项目中,我们每天都会和设计师和产品负责人讨论关于平衡美观和性能的问题。对于我们自己的网站,这样做是很简单的。简言之,我们认为好的用户体验从快速的内容传输开始,也就意味着 性能 > 美观

好的内容、布局、图片和交互是吸引用户的重要因素。这每个因素都会影响页面的加载时间和终端用户体验。每一步我们都在探讨如何在获得好的用户体验和保证设计美感的同时,最小化对性能的影响。

1.资源缓存

Image Delivery

图片是网站的不可或缺的部分,其能够大大提升网站的表现力与视觉效果,而目前平均大小为2406KB的网页中就有1535KB是图片资源,可见图片占据了静态资源多么大的一个比重,这也是我们需要重点优化的部分。
澳门新浦京娱乐场网站 2

可控性

给理想网站定义了标准后,我们总结出:要想达到预期效果,就要能对网站各方面的控制都游刃有余。我们选择构建自己的静态站点生成器,包括资源传输,并且由我们自己操控。

内容优先

我们想要把核心内容尽快地呈现给用户,意味着我们要处理好基本的 html 和 css。每个页面都应该达到基本的目的:传递信息。js、css、网页字体、图片、网站分析等优化都是位居于核心内容之下的。

内容优先

我们想要把核心内容尽快地呈现给用户,意味着我们要处理好基本的 HTML 和 CSS。每个页面都应该达到基本的目的:传递信息。JS、CSS、网页字体、图片、网站分析等优化都是位居于核心内容之下的。

1.1 使用CDN

将网站的静态资源分离,如静态HTML、图片Image、样式CSS、脚本JS等,把静态资源部署到CDN中,可以明显加快这部分资源的加载速度。

WebP

WebP 是面向现代网页的高压缩低损失的图片格式,通常会比JPEG小25%左右。然后WebP目前被很多人忽视,也不常使用。截止到本文撰写的时候,WebP目前只能够在Chrome, Opera and Android (大概占用户数的 50%)这些浏览器中使用,不过我们还是有办法以JPG/PNG来弥补部分浏览器中不支持WebP的缺憾。

静态站点生成器

我们用 Node.js 实现了静态站点生成器。它是采用带有简短 JSON 页面描述标签的Markdown 文件来生成整个网站结构和它所有的资源。为了包括特殊的页面脚本,也可以附带一个 HTML 文件。以下是一个简单化的描述标签和 markdown 文件,用于博客的发布,用它来生成真正的 HTML

JSON 描述标签:

JavaScript

{ "keywords": ["performance", "critical rendering path", "static site", "..."], "publishDate": "2016-07-13", "authors": ["Declan"] }

1
2
3
4
5
{
  "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...

1
2
3
4
5
# 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...
可控性

给理想网站定义了标准后,我们总结出:要想达到预期的效果,就要能对网站各方面的控制都游刃有余。我们选择构建自己的静态站点生成器,包括资源传输,并且由我们自己操控。

可控性

给理想网站定义了标准后,我们总结出:要想达到预期效果,就要能对网站各方面的控制都游刃有余。我们选择构建自己的静态站点生成器,包括资源传输,并且由我们自己操控。

1.2 利用HTTP缓存机制

HTTP缓存会把浏览器加载过的资源缓存到本地,下次加载时,只要缓存的资源没有过期,就可以直接使用本地的资源,减少了HTTP请求次数,加快了资源加载速度。具体做法是设置HTTP Header 中的Cache-Control参数。HTTP 1.0 中使用Pragma和Expires两个参数进行缓存,不过早已不推荐使用。

picture标签

使用picture标签可以方便的对于WebP格式不支持的情况下完成替换:

XHTML

<picture> <source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)"> <source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)"> <source type="image/webp" srcset="image-s.webp"> <source srcset="image-l.jpg" media="(min-width: 640px)"> <source srcset="image-m.jpg" media="(min-width: 320px)"> <source srcset="image-s.jpg"> <img alt="Description of the image" src="image-l.jpg"> </picture>

1
2
3
4
5
6
7
8
9
<picture>
  <source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)">
  <source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)">
  <source type="image/webp" srcset="image-s.webp">
  <source srcset="image-l.jpg" media="(min-width: 640px)">
  <source srcset="image-m.jpg" media="(min-width: 320px)">
  <source srcset="image-s.jpg">
  <img alt="Description of the image" src="image-l.jpg">
</picture>

这里我们使用了 picturefill by Scott Jehl作为Polyfill库来保证低版本的浏览器中能够支持picture标签,并且保证跨浏览器的功能一致性。并且我们还使用了img标签来保证那些不支持picture的浏览器能够正常工作。

图片传输

平均一个 2406kb 的网页中 1535kb 是图片。就因为图片在网站中占据了这么大的一个比例,所以它也是性能优化的重点之一。

澳门新浦京娱乐场网站 3

静态站点生成器

我们用 Node.js 实现了静态站点生成器。它是采用带有简短 JSON 页面描述标签的 Markdown 文件来省城整个网站结构和它所有的资源。为了包括特殊的页面脚本,也可以附带一个 HTML 文件。以下是一个简单化的描述标签和 markdown 文件,用于博客的发布,用它来生成真正的 HTML。

JSON 描述标签:

{
  "keywords": ["performance", "critical rendering path", "static site", "..."],
  "publishDate": "2016-08-29",
  "authors": ["Simon"]
}

markdown 文件:

# Why our website is faster than yours 
We've recently updated site. Yes, it has a complete...

## Design for performance
In our projects we have daily discussions...

静态站点生成器

我们用 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...

2. 资源的合并压缩

图片多格式生成

现在我们已经可以通过设置不同的图片尺寸、格式来保证图片的分发优化,不过我们总不希望每次要用一张图片的时候就去生成6个不同的尺寸/实例。我们希望有一种抽象的方法可以帮我们自动完成这一步,为我们自动生成不同的格式/尺寸,然后自动插入合适的picture元素,在我们的静态网站生成器中是这么做的:

  • 首先是要gulp responsive来生成不同尺寸的图片,该插件同样会输出WebP格式的图片
  • 压缩生成好的图片
  • 用户只需要在MarkDown中编写![Description of the image](image.jpg)即可
  • 我们自定义的MarkDown渲染引擎会在处理过程中自动使用picture元素替换这些img标签

WebP格式

WebP是一种现代图片格式,为网页图片提供了出色的低损耗、有损压缩。WebP格式的图片实质上比其它格式的小,有时可以比同样的 JPEG 图片小 25%。 WebP被大多数人所忽略,也没被经常使用。截止到写这篇文章的时候,WebP 仅支持Chrome, Opera 和 Android (仍超过了我们50%的用户),但我们可以优雅降级为 JPG/PNG。

使用 <picture> 元素我们可以把图片从 WebP 优雅地降级到其它被广泛支持的图片格式,如JPEG:

XHTML

<picture> <source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)"> <source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)"> <source type="image/webp" srcset="image-s.webp"> <source srcset="image-l.jpg" media="(min-width: 640px)"> <source srcset="image-m.jpg" media="(min-width: 320px)"> <source srcset="image-s.jpg"> <img alt="Description of the image" src="image-l.jpg"> </picture>

1
2
3
4
5
6
7
8
9
<picture>
  <source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)">
  <source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)">
  <source type="image/webp" srcset="image-s.webp">
  <source srcset="image-l.jpg" media="(min-width: 640px)">
  <source srcset="image-m.jpg" media="(min-width: 320px)">
  <source srcset="image-s.jpg">
  <img alt="Description of the image" src="image-l.jpg">
</picture>

我们使用Scott Jehl 的 picturefill 来使那些不支持 <picture> 元素的浏览器获得支持,在各个浏览器中达到一致的效果

我们使用 <img> 作为那些不支持 <picture> 或者 JS 的浏览器的后备元素。使用图片的最大实例确保了它在后备方案中的可行性。

图片传输

平均一个2406kb的网页中1535kb是图片。就因为图片在网站中占据了这么大的一个比例,所以它也是性能优化的重点之一。

澳门新浦京娱乐场网站 4

QQ20160829-0@2x.png

图片传输

平均一个 2406kb 的网页中 1535kb 是图片。就因为图片在网站中占据了这么大的一个比例,所以它也是性能优化的重点之一。

澳门新浦京娱乐场网站 5

2.1 减少HTTP请求

用一个HTTP请求去加载一个10M的文件,和把这个文件拆分成1M的10个文件,用10个HTTP请求并行去加载,哪一种方式能更快完成加载?既然提到减少HTTP请求可以提高网站响应速度,那么结论貌似应该是用一个HTTP请求的方式更快。其实正确的答案是:不一定!

我做了一个小实验:有两个html文件,index1.html和index2.html,index1.html中用1个<script>标签加载一个2M的js文件bundle.js,index2.html中用6个<script>标签分别加载bundle1.js, bundle2.js …… bundle6.js,这6个js文件由bundle.js平均拆分得到。分别请求index1.html和index2.html 10次,得到加载bundle.js的时间和加载bundle1.js 到 bundle6.js的时间(以最后一个js文件加载完成为结束时间),计算平均加载时间分别为:1.07s 和 1.87s。

实验结论证明了,一个HTTTP请求加载一个合并后的资源文件,比多个HTTTP请求并发加载多个资源文件效率高。但结论只是针对平均加载时间而言,对于单次的比较,完全可能出现相反的结论,例如我的实验过程中,单一HTTTP请求加载时间的最大值为2.36s,超过了第二种加载方式的平均时间1.87s。可能有些人会比较疑惑,为什么并行的效率反而比串行的要低呢?其实,HTTP请求加载资源的瓶颈在带宽,而不是请求的数量,在一个请求已经利用带宽很充分的情况下,增加新的请求并不能减少整体的资源加载时间。

其实,减少HTTP请求来提高网站性能主要是基于以下2个原因:

1) HTTP连接的建立是比较耗时的,一般需要上百ms,每个HTTP请求还有一定的网络延时,需要的HTTP请求越多,这两部分产生的耗时也就越多。当然,HTTP 1.1 对keep-alive的默认支持,可以实现连接的复用,很大程度上优化了这个问题。

2)每个HTTP请求都需要附带额外的数据,比如请求和响应中的头信息,Cookie信息。当请求的资源很小时,附带的额外数据可能比实际的资源还大。

SVG Animation

我们的网站中也存在着很多的Icon以及动画性质图片,这里我们是选择SVG作为Icon与Animation的格式,主要考虑有下:

  • SVG是矢量表示,往往比位图文件更小
  • SVG自带响应式功效,能够根据容器大小进行自动缩放,因此我们不需要再为了picture元素生成不同尺寸的图片
  • 最重要的一点是我们可以使用CSS去改变其样式或者添加动画效果,关于这一点可以参考CodePen上的这个演示 点击预览 。
    澳门新浦京娱乐场网站 6

生成

尽管图片传输方式已经确定了,我们仍需要思考该怎样有效地执行。我喜欢 <picture>元素的功能,但不喜欢写上面那些代码段,尤其是写内容时必须把它加进去。我们不想做这么费力的事情:每张图片都要写 6 个实例,所以优化这些图片并且把它们写在markdown文件的 <picture> 里面。所以:

WebP格式

WebP 是一种现代图片格式,为网页图片提供了出色的低损耗、有损压缩。WebP 格式的图片实质上比其它格式的小,有时可以比同样的 JPEG 图片小25%。WebP 被大多数人所忽略,也没被经常使用。截止到目前,WebP 仅支持chrome,opera 和 android,但我们可以优雅降级为 JPG/PNG。

使用 <picture> 元素我们可以把图片从 WebP 优雅地降级到其它被广泛支持的图片格式,如JPEG:

<picture>
    <source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)">
    <source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)">
    <source type="image/webp" srcset="image-s.webp">
    <source srcset="image-l.jpg" media="(min-width: 640px)">
    <source srcset="image-m.jpg" media="(min-width: 320px)">
    <source srcset="image-s.jpg" >
    <img alt="Description of the image" src="image-l.jpg">
</picture>

我们使用Scott Jehl 的 picturefill 来使那些不支持 <picture> 元素的浏览器获得支持,在各个浏览器中达到一致的效果.

我们使用<img/>作为那些不支持 <picture> 或者 JS 的浏览器的后备元素。使用图片的最大实例确保了它在后备方案中的可行性。

WebP格式

WebP是一种现代图片格式,为网页图片提供了出色的低损耗、有损压缩。WebP格式的图片实质上比其它格式的小,有时可以比同样的 JPEG 图片小 25%。 WebP被大多数人所忽略,也没被经常使用。截止到写这篇文章的时候,WebP 仅支持Chrome, Opera 和 Android (仍超过了我们50%的用户),但我们可以优雅降级为 JPG/PNG。

使用 <picture> 元素我们可以把图片从 WebP 优雅地降级到其它被广泛支持的图片格式,如JPEG:

<picture>
  <source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)">
  <source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)">
  <source type="image/webp" srcset="image-s.webp">
  <source srcset="image-l.jpg" media="(min-width: 640px)">
  <source srcset="image-m.jpg" media="(min-width: 320px)">
  <source srcset="image-s.jpg">
  [站外图片上传中……(7)]
</picture>

我们使用Scott Jehl 的 picturefill 来使那些不支持 <picture> 元素的浏览器获得支持,在各个浏览器中达到一致的效果

我们使用 <img> 作为那些不支持 <picture> 或者 JS 的浏览器的后备元素。使用图片的最大实例确保了它在后备方案中的可行性。

2.2 JS文件

合并压缩JS文件,一方面JS文件数量减少,需要的HTTP请求数也就减少了;另一方面,压缩JS文件可以极大地减小文件体积。可以使用webpack等Web构建工具对JS文件进行压缩合并。

要注意,压缩合并JS文件并不是要把所有的JS文件都打包到一个JS文件中。一般的做法是按照“基础代码” “页面代码”分别打包。“基础代码”指各个页面或路由(对单页面而言)都要用到的通用代码,“页面代码”是只在某个具体页面或路由中才会用到的代码。这样就可以实现JS代码按需加载,避免页面首屏加载时,因为单一JS文件过大,而影响首屏显示时间。对单页面应用来说,还可以有一个vendor.js的文件,这个文件中的内容是一些用到频率比较高的第三方库(如ECharts等),但这些库并不是每个路由都会用到的,所以并不会被打包到“基础代码”中。将这样的第三方库从各个路由页面对应的JS文件中拆分,一是可以减少所有JS文件的整体大小,因为本来可能是A、B等多个文件都会包含的代码,现在则只需要一份;二是vendor.js只需要被加载一次,后续打开其他路由时,就可以不需要再次加载这部分代码了,起到了资源预加载的作用。

澳门新浦京娱乐场网站,Custom Web Fonts

我们首先回顾下浏览器是如何使用自定义字体的,当浏览器识别到用户在CSS中基于@font-size定义的字体时,会尝试下载该字体文件。而在下载的过程中,浏览器是不会展示该字体所属的文本内容,最终导致了所谓的Flash of Invisible Text现象。现在很多的网站都存在这个问题,这也是导致用户体验差的一个重要原因,即会影响用户最主要的内容浏览这一操作。而我们的优化点即在于首先将字体设置为默认字体,而后在自定义的Web Font下载完毕之后对标准字体再进行替换操作,并且重新渲染整个文本块。而如果自定义的字体下载失败,整个内容还是能保证基本的可读性,不会对用户体验造成毁灭性的打击。
澳门新浦京娱乐场网站 7

首先,我们会为需要使用到的Web Fonts创建最小子集,即只将那些需要使用的字体提取出来,而并不需要让用户下载整个字体集,这里推荐使用Font squirrel webfont generator。另外,我们还需要为字体的下载设置监视器,即保证能够在字体下载完毕之后自动回调,这里我们使用的是fontfaceobserver,它会为页面自动创建一个监视器,在侦测到所有的自定义Web Fonts下载完毕后,会为整个页面添加默认的类名:

CSS

html {font-family: Georgia, serif;} html.fonts-loaded {font-family: Noto, Georgia, serif;}

1
2
html {font-family: Georgia, serif;}
html.fonts-loaded {font-family: Noto, Georgia, serif;}

不过现在CSS的font-display属性也原生提供了我们这种替换功能,更多详情可见font-display属性。

生成图片

在构建过程中,原图片的多个实例,包括JPG, PNG和WebP格式,我们使用 gulp responsive 来生成。

生成

尽管图片传输方式已经确定了,我们仍需要思考该怎样有效地执行。我喜欢 <picture> 元素的功能,但不喜欢写上面那些代码段,尤其是写内容时必须把它加进去。我们不想做这么费力的事情:每张图片都要写 6 个实例,所以优化这些图片并且把它们写在markdown文件的 <picture> 里面。所以:

生成

尽管图片传输方式已经确定了,我们仍需要思考该怎样有效地执行。我喜欢 <picture> 元素的功能,但不喜欢写上面那些代码段,尤其是写内容时必须把它加进去。我们不想做这么费力的事情:每张图片都要写 6 个实例,所以优化这些图片并且把它们写在markdown文件的 <picture> 里面。所以:

2.3 CSS文件

对CSS文件进行合并压缩,基本原理和做法同JS文件。

JS 与 CSS 的懒加载

总的来说我们希望所有的资源能够尽可能快地加载完毕,不过往往为了保证首页加载的速度,我们会考虑将部分非首屏需要的JS/CSS文件进行延迟加载,或者对于重复的视图使用浏览器本地缓存。

最小化图片

生成图片

在构建过程中,原图片的多个实例,包括 jpg、png 和 webp 格式,我们使用 gulp responsive 来生成。

生成图片

在构建过程中,原图片的多个实例,包括JPG, PNG和WebP格式,我们使用 gulp responsive 来生成。

2.4 图片

1) 使用WebP格式的图片。WebP是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8。根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这些 PNG 文件经过其他压缩工具压缩之后,WebP 还是可以减少 28% 的文件大小。

2)使用字体图标IconFont。可以任意设置Icon图形的大小和颜色(只能是单色,因为本质上是给字体设置颜色)。

3)使用CSS Sprites将多张图片合并成一张,从而减少HTTP请求数量。

4)使用Base64直接把图片编码成字符串写入CSS文件,也是从减少HTTP请求数量考虑。但需要注意,Base64编码的图片最好是小图片(最好几十字节级别的),因为图片经过Base64编码后,一般会比原文件更大些。而且太长的Base64编码字符串也会影响CSS的整体可读性。

5)对于需要大量图片的网站,应该把图片资源单独部署,并使用不同的域名来访问。因为图片资源占带宽很大,如果把图片和其他资源部署到一台服务器或一个集群中,服务器端的出口带宽会受到很大影响。使用不同的域名加载图片资源,可以更好的利用浏览器并行下载的特性,因为浏览器对于一个域名下的最大并行请求数是有限制的。

Lazy Load JS

目前来说,我们的网站都是偏向于静态,并不需要太多的JavaScript介入,不过考虑到日后的扩展空间,我们还是构建了一套完整的JS的工作流。众所周知,如果将JS直接放置到head标签中,其会阻塞整个页面的渲染。对于该点,最简单的方式就是将会阻塞渲染的JS脚本移动到页面的尾部,在整个首屏渲染完毕之后再进行加载。另一个常用的手段就是依然保持JS文件位于head标签中,不过为其添加一个defer的属性,这保证了浏览器只会先将该脚本下载下来,然后等到整个页面加载完毕再执行该脚本。
另一个需要注意的是,因为我们并不使用类似于jQuery这样的第三方依赖库,而更多的依赖于浏览器原生的特性,因此我们希望在合适的浏览器内加载合适版本的JS代码,其效果大概如下:

XHTML

<script> // Mustard Cutting if ('querySelector' in document && 'addEventListener' in window) { document.write('<script src="index.js" defer></script>'); } </script>

1
2
3
4
5
6
<script>
// Mustard Cutting
if ('querySelector' in document && 'addEventListener' in window) {
  document.write('<script src="index.js" defer></script>');
}
</script>

在markdown文件中写[图片描述](image.jpg).

在构建过程中使用自定义Markdown渲染器来为已经完全成熟的 <picture> 元素编译传统的markdown图片声明。

最小化图片

在 markdown 文件中写 图片描述

最小化图片

2.5 服务器端开启gzip

服务端开启gzip压缩,可以减少资源文件在网络传输过程中的体积大小。

Lazy Load CSS

正如上文所述,我们的网站偏向于静态展示,因此首屏的最大问题就是CSS文件的加载问题。浏览器会在head标签中声明的所有CSS文件下载完毕之前一直处于阻塞状态,这种机制很是明智的,不然的话浏览器在加载多个CSS文件的时候会进行重复的布局与渲染,这更是对于性能的浪费。
为了避免非首屏的CSS文件阻塞页面渲染,我们使用loadCSS这个小的工具库来进行异步的CSS文件加载,它会在CSS文件加载完毕后执行回调。不过,异步加载CSS也会带来一个新的问题,如果我们将所有的CSS全部设置为了异步加载,那么用户会首先看到单纯的HTML页面,这也会给用户不好的体验。那么我们就需要在异步加载与首屏渲染之间找到一个平衡点,即首先加载那些必要的CSS文件。
我们一般将首屏渲染中必要的CSS文件成为Critical CSS,即关键的CSS文件,代指在保证页面的可读性的前提下需要加载的最少的CSS文件数目。Critical CSS的选定会是一个非常耗时的过程,特别是我们网站本身的CSS样式设置也在不停变更,我们不可能完全依赖于人工去提取出关键的CSS文件,这里推荐Critical这个辅助工具能够帮你自动提取压缩Critical CSS。下图的一个对比即是仅加载Critical CSS与加载全部CSS的区别:

澳门新浦京娱乐场网站 8

上图中红色的线,即是所谓的折叠分割点。

SVG动画

我们为自己的网站选择了特定的图标类型,其中SVG插图占了主要地位。这样做有以下几个原因:

  • 首先,SVG的图片比位图更小;
  • 其二,SVG图片本身就是响应式的,有很棒的伸缩性, 所以不需要图片生成及<picture> 元素;
  • 最后也是很重要的一点,就是我们可以通过CSS来不断改变它,赋予它新的活力!我们所有的组合页面都有一个自定义的动态SVG图, 可以被概述页面所复用。这张图片作为我们所有组合页面的一种循环风格,使得页面设计一体化,同时又几乎不会对性能造成影响。请看这张动画,看看我们是如何用CSS来改变它的。

澳门新浦京娱乐场网站 9

SVG动画

我们为自己的网站选择了特定的图标类型,其中SVG插图占了主要地位。这样做有以下几个原因:

首先,SVG的图片比位图更小;

澳门新浦京娱乐场网站:从案例剖析哪些优化前端品质,网址品质优化你需掌握的事物。其二,SVG图片本身就是响应式的,有很棒的伸缩性, 所以不需要图片生成及 <picture> 元素;

最后也是很重要的一点,就是我们可以通过CSS来不断改变它,赋予它新的活力!我们所有的组合页面都有一个自定义的动态SVG图, 可以被概述页面所复用。这张图片作为我们所有组合页面的一种循环风格,使得页面设计一体化,同时又几乎不会对性能造成影响。请看这张动画,看看我们是如何用CSS来改变它的。

澳门新浦京娱乐场网站 10

0.gif

自定义网页字体
澳门新浦京娱乐场网站:从案例剖析哪些优化前端品质,网址品质优化你需掌握的事物。在深入之前,这里有一个关于在浏览器设置自定义字体的简短介绍。当浏览器发现CSS里面有@font-face 的定义,但是用户的电脑并不支持该字体时,它会尝试下载该字体文件。在下载时,多数浏览器根本不会用这种字体来展示文本。这种现象称为“不可见文本的闪现” 或者 FOIT。如果你有留意,你会发现网页上都有这种情况存在。如果你问我,我会告诉你这会影响用户体验。它延迟了用户读取他们所需内容的时间。我们可以迫使浏览器改变这种行为,变成 “无样式内容闪现” 或者称为 FOUT。我们告诉浏览器先使用普通字体,像 Arial 或者 Georgia。当自定义的字体下载完成后,再代替标准字体并且重新渲染。这样,即使自定义字体下载失败,仍然不会影响内容的可读性。然而,有人会认为这是一种妥协的做法,但我们认为自定义字体只是一种优化。尽管没有自定义字体,网页看起来也完好,也能百分百的正常运行。勾选/不勾选复选框来切换我们的网页字体,来自己体验一下:

切换下载的字体类 使用自定义网页字体可以改善我们的用户体验,只要你能够优化他们,并且负责任地为之服务。

字型子集设定
到目前为止,子集设定以最短的时间在改善网页字体性能方面取胜。我将会向每个使用自定义字体的网页开发者推荐子集设定。如果你能完全控制网页内容,并且知道它将要展示哪些特性的话,你可以完全使用子集设定。但是,即使是仅仅把字体设为西方语言,也会对文件大小造成很大的影响。例如,我们的 Noto Regular WOFF 字体,默认是246KB,将其设为西方语言后,大小下降到31KB。我们使用 Font squirrel webfont, 这种字体真的很易用。

字体监听器
Bram Stein 推出的字体监听器是一个很了不起的脚本,可以帮助检查字体是否已被加载。至于你是如何加载字体的,是通过一个网页字体服务,还是自己上传就不可知了。在这个监听器告诉我们所有自定义的字体已经下载完毕后,我们就可以在 <html> 元素上添加一个字体加载完毕的类,我们的页面就会重新用新的字体:

html {
    font-family: Georgia, serif;
}
html.fonts-loaded {
    font-family: 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在 <head> 块里面渲染,这是我们想要的。JS应该只是用来提高用户体验,不应该是访问者需要的关键。处理JS堵塞渲染的简单方法就是把脚本放在页面的尾部。这样网页就会在整个HTML 渲染完毕后才去加载JS。

另一种可以把脚本放在 head 执行的方案是在 <script> 标签里面添加 defer 属性,来延迟脚本的执行。由于浏览器下载脚本是很快的,不会堵塞页面渲染进程,等到页面完全加载完,才会执行脚本里面的代码。还有一件事,我们没有使用像jQuery这些库,所以我们的脚本取决于 vanilla 脚本的特性。我们只是想要在浏览器加载脚本来支持这些特性。最终的结果就像下面的代码这样:

<script>
    if('querySelector' in document && 'addEventListener' in window) {
        document.write('<script src="index.js" defer></script>');
    }
</script>

我们把这小段脚本放在页面头部,来检测浏览器是否支持 vanilla JavaScript 的 document.querySelector 和 window.addEventListener 属性。如果支持,我们通过 <script> 标签直接给页面加载脚本,并使用 defer 属性让它不会堵塞页面渲染。

懒加载CSS
对于首屏来讲,网站最大的渲染堵塞资源就是CSS了。只有当 <head> 里面的CSS文件完全加载完毕时,浏览器才会开始渲染页面。这种做法是经过深思熟虑的,若不是这样,浏览器就需要在整个渲染过程中不断重新计算布局尺寸,不断重绘。

为了防止CSS堵塞渲染,我们就需要异步加载CSS文件。我们使用了 Filament Group 的 awesome loadCSS 函数。该函数提供了一个回调,当CSS文件加载完后,我们设置一个cookie来声明CSS文件已经加载了。我们使用这个cookie来重现页面,这一点我后续会解释到。

CSS异步加载也带来这样一个问题,尽管 HTML 真的很快被渲染出来,但看起来就只有纯粹的HTML,只有等到整个CSS文件加载完且停止时,才会有样式。这时就要提到关键CSS了。

关键部分CSS
关键部分CSS可以这样来理解:它就是阻塞浏览器渲染出用户可识别的网页的一小部分CSS。我们注意网页的 上半版版面。很明显,两个设备的版面的位置有很大的区别。因此,我们做了一个大胆的猜测。

手动地检测这部分关键性的CSS是个很耗时的过程,尤其是样式、特性等不断改变时。这里有几个好的脚本,可以在你构建网页时,生成关键性CSS。我们采用了 Addy Osmani 的版本。

下面是我们分别用关键性CSS和整份CSS分别渲染的页面效果。注意到下半版仍然有部分内容还没有样式。

澳门新浦京娱乐场网站 11

QQ20160829-0@2x.png

左侧网页仅仅是渲染了关键性CSS,而右侧网页则是渲染了整份CSS。红线是分界线。

服务端
我们自己部署 de Voorhoede 网站,因为我们希望能够控制服务器环境。我们也想要尝试,是否可以通过改变服务端的配置来大大提升性能。当前我们使用了 Apache 服务和 HTTPS 协议。

配置
为了提升性能和安全性,我们研究了如何配置服务端。

我们使用 H5BP apache样板配置,这个对于改善Apache网络服务的性能和安全性是个很好的开始。他们也有其他服务器环境的配置。对于我们的大部分 HTML、CSS 和 JS,我们使用GZIP压缩。对于我们的所有网站资源,都使用缓存HTTP标头的做法。请阅读下面关于缓存的章节。

HTTPS
用HTTPS来服务你的网站会对性能造成影响。这主要是设置了SSL握手,引入了大量潜在的东西。但通常情况下,我们可以做一些改变!

HTTP Strict Transport Security 是一个HTTP标头,可以让服务器告诉浏览器只能用HTTPS来与它交互。这种方式防止HTTP请求被重定向为HTTPS。所有尝试用HTTP来访问站点的请求都会被自动转换成HTTPS。它可以来回的服务我们的网站。

TLS false start 允许客户端在第一个TLS回合结束后,马上开始向后端发送加密的数据。这种优化为给一个回合新建一个TLS连接减少了握手。一旦客户端知道了密钥,就可以开始传输应用数据。剩下的握手用来确认是否有人篡改了握手记录,并且可以并行处理。

TLS session resumption 通过确认浏览器和服务器是否已经通过TLS取得联系来帮我们节省另一个来回。浏览器会记住这一次的标识符,下次发起连接时,这个标识符就会被重用,节省了一个来回。

我听起来像是一个搞开发和运维的,但确实不是。我只是读过一些书,看过一些视频。我喜欢 Mythbusting HTTPS: 被 Google I/O 2016 的 Emily Stark 镇压的安全性都市传奇。

cookies的使用
我们没有用一门服务端的语言,只有静态的 Apache 网络服务。但一个 Apache 网络服务仍可以做包括SSI在内的后端服务,并且读取cookies。通过巧用cookies和运行那部分被Apache改写的HTML,我们可以大大提升前端性能。下面这个例子就是了(我们实际的代码比这个复杂点,但是思想上是一致的):

<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css' )" -->
<noscript>
    <link rel="stylesheet" href="0d82f.css">
</noscript>
<script>
    (function(){
        function loadCSS(url) {...}
        function onloadCSS(stylesheet, callback) {...}
        function setCookie(name, value, expInDays) {...}

        var stylesheet = loadCSS('0d82f.css');
        onloadCSS(stylesheet, function(){
            setCookie('css-loaded', '0d82f', 100);
        });
    }());
</script>
<style>/* Critical CSS here */</style>

<!-- #else -->
<link rel="stylesheet" href="0d82f.css">
<!-- #endif -->

The Apache server side logic are the comment looking lines starting with Apache 服务端逻辑是一些看起来一行一行的评论,一般这样开始:

第一次浏览我们添加了 <noscript> 标签,里面放置了 <link rel="stylesheet">。之所以这样做,是因为我们要异步加载整份CSS和JS。如果JS不能用的话,这种做法是不能执行的。这意味着,我们要用常规的加载CSS的方法来做回退。

我们添加了一个行内的脚本来懒加载CSS,onloadCSS 回调里面可以设置cookies.

在同一份脚本里面,我们异步加载了整份CSS。

在 onloadCSS的回调里面,我们用版本号来设置cookie的值。

在这个脚本后面,我们添加了一行关键CSS的样式。这个会阻塞渲染,但时间是非常短的,而且可以避免页面展示出来只有纯粹的HTML而没有样式的情况。

声明(意味着 css-loaded 的cookie 已经存在)用户重复浏览。因为我们可以从某种程度上来假定,CSS文件之前已经被加载过了,我们可以利用浏览器缓存来提供样式表。这样从缓存里面加载速度就很快了。同样的方法也被用来在第一次异步加载字体,后续的重复浏览也是从缓存里面获取字体。

澳门新浦京娱乐场网站 12

QQ20160829-1@2x.png

这就是我们第一次和重复浏览时,我们用来区分的cookies。

文件级缓存
由于我们在重现页面时很大程度上依赖于浏览器缓存,所以我们需要确认我们的缓存是否合理。理想中我们是要永远的存储资源(CSS、js、 字体、图片),只有当这些文件被修改时才需要更新。当请求的URL是唯一时,缓存就会失效。每更新一个版本,我们都会用 git tag 打个标签。所以最简单的方式就是给我们请求的URL加上一个参数(代码版本号),如 https://www.voorhoede.nl/assets/css/main-8af99277a6.css?v=1.0.4.。

但是,这种做法的缺点就是当我们要写一个新的博客post(这也是我们代码库的一部分,并没有永久地存储在CMS),原来缓存的资源将会失效,尽管没有改变原来那些资源。

就在我们尝试去改善这种方法时,我们发现了 gulp-rev 和 gulp-rev-replace 。这些脚本会自动合理地在我们的文件名称后面添加一窜hash值。这意味着只有实际上文件被改变时,才会去改变请求的URL,这样每个文件的缓存就会自动失效。这种做法让我兴奋不已啊!

成果
如果你看到这里,你应该是想要知道成果的。测试网页的性能可以采用像 PageSpeed Insights 这样的工具,它有很实用的提示。也可以使用 WebPagetest来测试,有扩展性的网络分析。我认为测试网页渲染性能的最好方法就是在疯狂地遏制网络通信时来观察网页的进程。这意味着,用一种不切实际的方法来遏制通信。在谷歌浏览器,你可以这样操作 (via the inspector > Network tab) 来限制通信,观察网页形成过程中,请求是如何缓慢加载的。

下面是我们的网页在 50KB/s 的情况下的加载状况。

澳门新浦京娱乐场网站 13

QQ20160829-2@2x.png

这是 de Voorhoede site 首屏的网络分析,是网页第一次进程的一个概览。

注意到在50KB/s的网速中,我们是如何让首屏的渲染只用了 2.27 秒的。也就是第一张幻灯片和瀑布图里面的黄色线所代表的位置。黄线恰好绘在就是HTML已经加载完的时间位置。HTML包含了关键CSS,保证页面的可观性。所有其他的CSS都是用懒加载,所以我们可以等到全部资源加载完时才与页面进行交互。这也是我们想要的效果!

另一个值得注意的就是自定义字体从来不在这缓慢的链接上加载。 font face 观察器会自动注意到这一点。但是,如果我们不异步加载字体,你注视大多数浏览器,都会出现 FOIT(不可见文本的闪现,上文有提及)的情况。

所有的CSS文件仅在8s后就都加载完毕。相反,如果我们不采用加载关键CSS的方式,而是采用加载全部CSS,我们在前8秒看到的将会是空白的页面。

如果你感到好奇,想与那些不太注重性能的网站对比一下加载时间,那赶紧试试吧。那个时间肯定是飞涨啊!

用上面介绍的工具测试了我们的网站,结果也是让人满意的。 PageSpeed insights 在移动性能方面给了我们100分,多么了不起啊!

PageSpeed insights 对 voorhoede.nl的测试结果! 速度100分!

当我们查看 WebPagetest 时,我们得到下面这样的结果:

澳门新浦京娱乐场网站 14

QQ20160829-3@2x.png

可以看出,我们的服务器运行良好,首屏的速度指标是693。 这意味着我们的页面在693毫秒后就可以在宽屏缆线下被使用了。

技术路线
我们这样还不算完成,还会不断地重复我们的方法。我不久的将来,我们会主要关注以下内容:

HTTP/2
目前我们正在试验HTTP/2。本文所描述的大多数东西都是基于 HTTP/1.1 权限内的最好实践。简言之,HTTP/1.1 要追述到1999年,那时 table布局和行内样式都如火如荼。HTTP/1.1 从没为 2.6MB的网页要接受200个请求而设计。为了减轻旧版协议带来的痛楚,我们结合JS、CSS、关键性CSS,还为小图片设置数据源URI等。用各种方法来节省请求。自从 HTTP/2 可以在同一个TCP链接上平行地运行多个请求后,所有的这些联结使用和减少请求的做法都可能成为反面模式了。当我们跑完这个实验后,我们将会采用 HTTP/2 协议。

Service Workers
这是一个在后台运行的现代浏览器的 JavaScript API。它拥有很多特性,这些特性在以前的网站上都是没有的,如离线支持、消息推送、背景同步等。我们现在正尝试用 Service Worker, 但还是得在我们自己的网站上实现先。我向你保证,我们会做到的!

CDN
因此,我们想要自己控制和部署我们的网站。而且现在我们也要采用CDN来摆脱由服务端和客户端实际距离所带来的网络问题。尽管我们的用户基本上都是荷兰的,我们也想向世界的前端社区反映我们在质量、性能和推动网络发展方面做的最好。

在markdown文件中写[图片描述](image.jpg).

在构建过程中使用自定义Markdown渲染器来为已经完全成熟的 <picture> 元素编译传统的markdown图片声明。

3.浏览器加载、解析、渲染机制

浏览器的工作原理非常繁琐和复杂,要想仔细了解,可以参考这篇经典的文章How browers work。

结合文章和我自己实验验证,简单来说的话,当浏览器载入一个HTML文件后,

1)会先将加载HTML中引用的所有外部资源(JS、CSS文件等)的请求放到一个队列中,然后浏览器通过多个线程(具体由浏览器设置决定)并发加载这些资源。

2)紧接着对HTML进行自上而下的解析。

3)当解析到<script>标签时,如果标签内是内嵌到HTML中的JS代码,会直接执行这部分代码;如果标签引用了外部的JS文件,且这个文件此时还没有下载完成,解析过程会被阻塞,直到JS文件下载完成,然后解析执行JS代码,之后才会继续HTML的解析过程;如果标签引用了外部的JS文件,但此时这个JS文件已经下载完成,则会直接执行这部分JS代码,并不会阻塞HTML的解析(可以理解成此时JS代码的执行本就属于HTML解析这个<script>标签的过程)。

4) 当解析到<link>标签时,不管<link>中引用的外部CSS资源是否加载完成,都不会阻塞HTML继续向下解析。

这里有2个需要注意的地方:

1)因为JS的加载会阻塞HTML向下解析,所以多个JS文件中代码的执行顺序,是和他们在HTML中的位置顺序保持一致的。例如HTML中,从上向下依次引入a.js, b.js, a.js的文件大小远大于b.js,这样b.js文件很可能先完成加载,但是并不会先于a.js中的代码执行,因为在a.js加载、解析、并执行完成前,HTML的解析是处于阻塞的,b.js所在的<script>标签自然也不会被解析执行。如果不希望加载外部JS文件阻塞HTML的解析,可以使用script标签的defer或async属性,这里就不再展开。

2)所有引用的外部脚本或样式文件,在HTML开始解析前,就已经加入到浏览器的请求队列中,所以多个外部资源开始加载的起始时间一般不会相差很大,除非请求的外部资源数量很多,超过了浏览器的并发请求数。

基于浏览器工作原理的常用优化性能的方法有2个:

1)引用外部CSS文件的link标签,一般会写在<head>内,这是为了能尽早的使<body>内的元素获取样式,优化视觉显示效果。

2)引用外部JS文件的script标签,一般会写在<body>底部,这是为了避免HTML的解析被阻塞,从而使页面元素更快的显示出来。需要注意,虽然script写在<body>底部,但这不意味着<body>内的其他元素都解析完成后才开始加载这些JS文件,这些JS文件依然会在HTML开始解析前,就被加入到请求队列中。

以上就是从资源缓存、资源合并压缩和浏览器解析原理三个维度出发,常用的优化网站性能的实践方法

 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入学习交流群

343599877,我们一起学前端!

服务端与缓存

高性能的前端离不开服务端的支持,在我们的实践中也发现不同的服务端配置同样会影响到前端的性能。目前我们主要使用Apache Web Server作为中间件,并且通过HTTPS来安全地传递内容。

自定义网页字体

在深入之前,这里有一个关于在浏览器设置自定义字体的简短介绍。当浏览器发现CSS里面有@font-face 的定义,但是用户的电脑并不支持该字体时,它会尝试下载该字体文件。在下载时,多数浏览器根本不会用这种字体来展示文本。这种现象称为“不可见文本的闪现” 或者 FOIT。如果你有留意,你会发现网页上都有这种情况存在。如果你问我,我会告诉你这会影响用户体验。它延迟了用户读取他们所需内容的时间。我们可以迫使浏览器改变这种行为,变成 “无样式内容闪现” 或者称为 FOUT。我们告诉浏览器先使用普通字体,像 Arial 或者 Georgia。当自定义的字体下载完成后,再代替标准字体并且重新渲染。这样,即使自定义字体下载失败,仍然不会影响内容的可读性。然而,有人会认为这是一种妥协的做法,但我们认为自定义字体只是一种优化。尽管没有自定义字体,网页看起来也完好,也能百分百的正常运行。勾选/不勾选复选框来切换我们的网页字体,来自己体验一下:

切换下载的字体类 使用自定义网页字体可以改善我们的用户体验,只要你能够优化他们,并且负责任地为之服务。

SVG动画

我们为自己的网站选择了特定的图标类型,其中SVG插图占了主要地位。这样做有以下几个原因:

  • 首先,SVG的图片比位图更小;

  • 其二,SVG图片本身就是响应式的,有很棒的伸缩性, 所以不需要图片生成及 <picture> 元素;

  • 最后也是很重要的一点,就是我们可以通过CSS来不断改变它,赋予它新的活力!我们所有的组合页面都有一个自定义的动态SVG图, 可以被概述页面所复用。这张图片作为我们所有组合页面的一种循环风格,使得页面设计一体化,同时又几乎不会对性能造成影响。请看这张动画,看看我们是如何用CSS来改变它的。

澳门新浦京娱乐场网站 15

Configuration

我们首先对于合适的服务端配置做了些调研,这里推荐是使用H5BP Boilerplate Apache Configuration作为配置模板,它是个不错的兼顾了性能与安全性的配置建议。同样地它也提供了面向其他服务端环境的配置。我们对于大部分的HTML、CSS以及JavaScript都开启了GZip压缩选项,并且对于大部分的资源都设置了缓存策略,详见下文的File Level Caching章节。

字型子集设定

到目前为止,子集设定是改善网页字体性能最快的方式。我将会向每个使用自定义字体的网页开发者推荐它。如果你能完全控制网页内容,并且知道它将要展示哪些特性的话,你可以完全使用子集设定。但是,即使是仅仅把字体设为西方语言,也会对文件大小造成很大的影响。例如,我们的 Noto Regular WOFF 字体,默认是246KB,将其设为西方语言后,大小下降到31KB。我们使用 Font squirrel webfont, 这种字体真的很易用。

自定义网页字体

在深入之前,这里有一个关于在浏览器设置自定义字体的简短介绍。当浏览器发现CSS里面有@font-face 的定义,但是用户的电脑并不支持该字体时,它会尝试下载该字体文件。在下载时,多数浏览器根本不会用这种字体来展示文本。这种现象称为“不可见文本的闪现” 或者 FOIT。如果你有留意,你会发现网页上都有这种情况存在。如果你问我,我会告诉你这会影响用户体验。它延迟了用户读取他们所需内容的时间。我们可以迫使浏览器改变这种行为,变成 “无样式内容闪现” 或者称为 FOUT。我们告诉浏览器先使用普通字体,像 Arial 或者 Georgia。当自定义的字体下载完成后,再代替标准字体并且重新渲染。这样,即使自定义字体下载失败,仍然不会影响内容的可读性。然而,有人会认为这是一种妥协的做法,但我们认为自定义字体只是一种优化。尽管没有自定义字体,网页看起来也完好,也能百分百的正常运行。勾选/不勾选复选框来切换我们的网页字体,来自己体验一下:

切换下载的字体类
使用自定义网页字体可以改善我们的用户体验,只要你能够优化他们,并且负责任地为之服务。

HTTPS

使用HTTPS可以保证站点的安全性,但是也会影响到你网站的性能表现,性能损耗主要发生在建立SSL握手协议的时候,这会导致很多的延迟,不过我们同样可以通过某些设置来进行优化。

  • 设置HTTP Strict Transport Security请求头可以让服务端告诉浏览器其只允许通过HTTPS进行交互,这就避免了浏览器从HTTP再重定向到HTTPS的时间消耗。
  • 设置TLS false start允许客户端在第一轮TLS中就能够立刻传递加密数据。握手协议余下的操作,譬如确认没有人进行中间人监听可以同步进行,这一点也能节约部分时间。
  • 设置TLS Session Resumption,当浏览器与服务端曾经通过TLS进行过通信,那么浏览器会自动记录下Session Identifier,当下次需要重新建立连接的时候,其可以复用该Identifier,从而解决了一轮的时间。

这里推荐扩展阅读下Mythbusting HTTPS: Squashing security’s urban legends by Emily Stark。

字体监听器

Bram Stein 推出的字体监听器是一个很了不起的脚本,可以帮助检查字体是否已被加载。至于你是如何加载字体的,是通过一个网页字体服务,还是自己上传就不可知了。在这个监听器告诉我们所有自定义的字体已经下载完毕后,我们就可以在 <html> 元素上添加一个字体加载完毕的类,我们的页面就会重新用新的字体:

CSS

html { font-family: Georgia, serif; } html.fonts-loaded { font-family: Noto, Georgia, serif; }

1
2
3
4
5
6
7
html {
   font-family: Georgia, serif;
}
 
html.fonts-loaded {
   font-family: Noto, Georgia, serif;
}

注意: 为了简短,我没有给上面CSS中的 Noto 加上 @font-face 的声明。

我们可以设定一个cookie来记住所有的字体已经被加载过,就可以让他们缓存在浏览器里面了。我们使用这个cookie来做重复的浏览,这个我后续会解释。

在不久的将来,我们或许不需要 Bram Stein 的脚本来监听这个行为。CSS开发团队已经提案一个新的 @font-face 描述器,也叫 font-display。它的属性值控制着一个可下载的字体是如何在还没加载出来时就渲染页面的。这是CSS对font-display的描述:它将带给我们像上面方法一样的行为效果。你可以读读更多关于 font-display 的属性。

字型子集设定

到目前为止,子集设定是改善网页字体性能最快的方式。我将会向每个使用自定义字体的网页开发者推荐它。如果你能完全控制网页内容,并且知道它将要展示哪些特性的话,你可以完全使用子集设定。但是,即使是仅仅把字体设为西方语言,也会对文件大小造成很大的影响。例如,我们的 Noto Regular WOFF 字体,默认是246KB,将其设为西方语言后,大小下降到31KB。我们使用 Font squirrel webfont, 这种字体真的很易用。

Cookies

我们并没有使用某个服务端框架,而是直接使用了静态的Apache Web Server,不过Apache Web Server也是能够读取Cookie并且进行些简单的操作。譬如在下面这个例子中我们将CSS缓存信息存放在了Cookie中,然后交付Apache进行判断是否需要重复加载CSS文件:

XHTML

<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css' )"--> <noscript><link rel="stylesheet" href="0d82f.css"></noscript> <script> (function() { function loadCSS(url) {...} function onloadCSS(stylesheet, callback) {...} function setCookie(name, value, expInDays) {...} var stylesheet = loadCSS('0d82f.css'); onloadCSS(stylesheet, function() { setCookie('css-loaded', '0d82f', 100); }); }()); </script> <style>/* Critical CSS here */</style> <!-- #else --> <link rel="stylesheet" href="0d82f.css"> <!-- #endif -->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css' )"-->
 
<noscript><link rel="stylesheet" href="0d82f.css"></noscript>
<script>
(function() {
  function loadCSS(url) {...}
  function onloadCSS(stylesheet, callback) {...}
  function setCookie(name, value, expInDays) {...}
 
  var stylesheet = loadCSS('0d82f.css');
  onloadCSS(stylesheet, function() {
    setCookie('css-loaded', '0d82f', 100);
  });
}());
</script>
 
<style>/* Critical CSS here */</style>
 
<!-- #else -->
<link rel="stylesheet" href="0d82f.css">
<!-- #endif -->

这里Apache Server中的逻辑控制代码就是有点类似于注释形式的<!-- #,其主要包含以下步骤:

  • $HTTP_COOKIE!=/css-loaded/ 检测是否有设置过CSS缓存相关的Cookie
  • $HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css'检测缓存的CSS版本是否为当前版本
  • If <!-- #if expr="..." --> 值为true 我们便能假设该用户是第一次访问该站点
  • 如果用户是首次浏览,我们添加了一个<noscript>标签,里面还包含了一个阻塞型的<link rel="stylesheet">标签。添加该标签的意义在于我们在下面是使用JavaScript来异步加载CSS文件,而在用户禁止JavaScript的情况下也能保证可以通过该标签来正常加载CSS文件。
  • <!-- #else --> 表达式在用户二次访问该页面时,我们可以认为CSS文件已经被加载过了,因此可以直接从本地缓存中加载而不需要重复请求。

上述策略同样可以应用于Web Fonts的加载,最终的Cookie如下所示:
澳门新浦京娱乐场网站 16

JS和CSS懒加载

一般来讲,我们都是尽可能快的加载需要的资源。我们移除一些堵塞的请求来加快页面渲染,优化首屏,用浏览器缓存来处理重复的页面。

字体监听器

Bram Stein 推出的字体监听器是一个很了不起的脚本,可以帮助检查字体是否已被加载。至于你是如何加载字体的,是通过一个网页字体服务,还是自己上传就不可知了。在这个监听器告诉我们所有自定义的字体已经下载完毕后,我们就可以在 <html> 元素上添加一个字体加载完毕的类,我们的页面就会重新用新的字体:

html {
   font-family: Georgia, serif;
}

html.fonts-loaded {
   font-family: Noto, Georgia, serif;
}

注意: 为了简短,我没有给上面CSS中的 Noto 加上 @font-face 的声明。

我们可以设定一个cookie来记住所有的字体已经被加载过,就可以让他们缓存在浏览器里面了。我们使用这个cookie来做重复的浏览,这个我后续会解释。

在不久的将来,我们或许不需要 Bram Stein 的脚本来监听这个行为。CSS开发团队已经提案一个新的 @font-face 描述器,也叫 font-display。它的属性值控制着一个可下载的字体是如何在还没加载出来时就渲染页面的。这是CSS对font-display的描述:它将带给我们像上面方法一样的行为效果。你可以读读更多关于 font-display 的属性。

File Level Caching

在上文可以发现,我们严重依赖于浏览器缓存来处理用户重复访问时资源加载的问题,理想情况下我们肯定希望能够永久地缓存CSS、JS、Fonts以及图片文件,然后在某个文件发生变化的时候将缓存设置为失效。这里我们设置了以https://www.voorhoede.nl/assets/css/main.css?v=1.0.4形式,即在请求路径上加上版本号的方式进行缓存。不过这种方式的缺陷在于如果我们更换了资源文件的存放地址,那么所有的缓存也就自然失效了。这里我们使用了gulp-rev以及gulp-rev-replace来为文件添加Hash值,从而保证了仅当文件内容发生变化的时候文件请求路径才会发生改变,即将每个文件的缓存验证独立开来。

JS懒加载

设计上,我们的网站并没有很多JS。我们发展了一个JavaScript工作流来处理我们目前已有的js, 以及未来会用到的js资源。

JS在 <head> 块里面渲染,这是我们想要的。JS应该只是用来提高用户体验,不应该是访问者需要的关键。处理JS堵塞渲染的简单方法就是把脚本放在页面的尾部。这样网页就会在整个HTML 渲染完毕后才去加载JS。

另一种可以把脚本放在 head 执行的方案是在 <script> 标签里面添加 defer 属性,来延迟脚本的执行。由于浏览器下载脚本是很快的,不会堵塞页面渲染进程,等到页面完全加载完,才会执行脚本里面的代码。还有一件事,我们没有使用像jQuery这些库,所以我们的脚本取决于 vanilla 脚本的特性。我们只是想要在浏览器加载脚本来支持这些特性。最终的结果就像下面的代码这样:

XHTML

<script> if ('querySelector' in document && 'addEventListener' in window) { document.write('<script src="index.js" defer></script>'); } </script>

1
2
3
4
5
<script>
if ('querySelector' in document && 'addEventListener' in window) {
  document.write('<script src="index.js" defer></script>');
}
</script>

我们把这小段脚本放在页面头部,来检测浏览器是否支持原生JavaScript的document.querySelectorwindow.addEventListener 属性。如果支持,我们通过<script> 标签直接给页面加载脚本,并使用 defer 属性让它不会堵塞页面渲染。

JS和CSS懒加载

一般来讲,我们都是尽可能快的加载需要的资源。我们移除一些堵塞的请求来加快页面渲染,优化首屏,用浏览器缓存来处理重复的页面。

Result

上面我们介绍了很多的优化手段,这里我们以实验的形式来对优化的结果与效果进行分析。我们可以用类似于PageSpeed Insights或者WebPagetest来进行性能测试或者网络分析。我觉得最好的测试你站点渲染性能的方式就是在限流的情况下观察页面的呈现效果,Google Chrome内置了限流的功能:
澳门新浦京娱乐场网站 17
这里我们将我们的网络环境设置为了50KB/S的GPRS网络环境,我们总共花费了2.27秒完成了首屏渲染。上图中黄线左侧的时间即指明了从HTML文件开始下载到下载完成所耗费的时间,该HTML文件中已经包含了关键的CSS代码,因此整个页面已经保证了基本的可用性与可交互型。而剩下的比较大的资源都会进行延时加载,这正是我们想要达到的目标。我们也可以使用PageSpeed来测试下网站的性能,可以看出我们得分很不错:
澳门新浦京娱乐场网站 18
而在WebPagetest中,我们看出了如下的结果:
澳门新浦京娱乐场网站 19

懒加载CSS

对于首屏来讲,网站最大的渲染堵塞资源就是CSS了。只有当 <head> 里面的CSS文件完全加载完毕时,浏览器才会开始渲染页面。这种做法是经过深思熟虑的,若不是这样,浏览器就需要在整个渲染过程中不断重新计算布局尺寸,不断重绘。

为了防止CSS堵塞渲染,我们就需要异步加载CSS文件。我们使用了 Filament Groupawesome loadCSS 函数。该函数提供了一个回调,当CSS文件加载完后,我们设置一个cookie来声明CSS文件已经加载了。我们使用这个cookie来重现页面,这一点我后续会解释到。

CSS异步加载也带来这样一个问题,尽管 HTML 真的很快被渲染出来,但看起来就只有纯粹的HTML,只有等到整个CSS文件加载完且停止时,才会有样式。这时就要提到关键CSS了。

JS懒加载

设计上,我们的网站并没有很多JS。我们发展了一个JavaScript工作流来处理我们目前已有的js, 以及未来会用到的js资源。

JS在 <head> 块里面渲染,这是我们想要的。JS应该只是用来提高用户体验,不应该是访问者需要的关键。处理JS堵塞渲染的简单方法就是把脚本放在页面的尾部。这样网页就会在整个HTML 渲染完毕后才去加载JS。

另一种可以把脚本放在 head 执行的方案是在 <script> 标签里面添加 defer 属性,来延迟脚本的执行。由于浏览器下载脚本是很快的,不会堵塞页面渲染进程,等到页面完全加载完,才会执行脚本里面的代码。还有一件事,我们没有使用像jQuery这些库,所以我们的脚本取决于 vanilla 脚本的特性。我们只是想要在浏览器加载脚本来支持这些特性。最终的结果就像下面的代码这样:

<script>
if ('querySelector' in document && 'addEventListener' in window) {
  document.write('<script src="index.js" defer></script>');
}
</script>

我们把这小段脚本放在页面头部,来检测浏览器是否支持原生JavaScript的 document.querySelectorwindow.addEventListener 属性。如果支持,我们通过 <script> 标签直接给页面加载脚本,并使用 defer 属性让它不会堵塞页面渲染。

Roadmap

优化之路漫漫,永无止境,我们在未来也会关注以下几个方面:

  • HTTP/2:我们目前已经开始尝试使用HTTP/2,而本篇文章中提到的很多的优化的要点都是面向HTTP/1.1的。简言之,HTTP/1.1诞生之初还是处于Table布局与行内样式流行的时代,它并没有考虑到现在所面对的2.6MB大小,包含200多个网络请求的页面。为了弥合这老的协议的缺陷,我们不得不连接JS与CSS文件、使用行内样式、对于小图片使用Data URL等等。这些操作都是为了节约请求次数,而HTTP/2中允许在同一个TCP请求中进行多个并发的请求,这样就会允许我们不需要再去进行大量的文件合并操作。
  • Service Workers:这是现代浏览器提供的后台工作线程,可以允许我们为网站添加譬如离线支持、推送消息、后台同步等等很多复杂的操作。
  • CDN:目前我们是自己维护网站,而在真实的应用场景下可以考虑使用CDN服务来减少服务端与客户端之间的物理距离,从而减少传输时延。

    2 赞 5 收藏 评论

澳门新浦京娱乐场网站 20

关键CSS

关键CSS就是阻塞浏览器渲染出用户可识别的网页的一小部分CSS。我们注意网页的 上半版版面。很明显,两个设备的版面的位置有很大的区别。因此,我们做了一个大胆的猜测。

手动地检测这部分关键性的CSS是个很耗时的过程,尤其是样式、特性等不断改变时。这里有几个好的脚本,可以在你构建网页时,生成关键性CSS。我们采用了 Addy Osmani的版本。

下面是我们分别用关键性CSS和整份CSS分别渲染的页面效果。注意到下半版仍然有部分内容还没有样式。

澳门新浦京娱乐场网站 21

左侧网页是用关键CSS渲染的,而右侧网页则是用整份的CSS。红线是分界线。

懒加载CSS

对于首屏来讲,网站最大的渲染堵塞资源就是CSS了。只有当 <head> 里面的CSS文件完全加载完毕时,浏览器才会开始渲染页面。这种做法是经过深思熟虑的,若不是这样,浏览器就需要在整个渲染过程中不断重新计算布局尺寸,不断重绘。

为了防止CSS堵塞渲染,我们就需要异步加载CSS文件。我们使用了 Filament Groupawesome loadCSS 函数。该函数提供了一个回调,当CSS文件加载完后,我们设置一个cookie来声明CSS文件已经加载了。我们使用这个cookie来重现页面,这一点我后续会解释到。

CSS异步加载也带来这样一个问题,尽管 HTML 真的很快被渲染出来,但看起来就只有纯粹的HTML,只有等到整个CSS文件加载完且停止时,才会有样式。这时就要提到关键CSS了。

服务端

我们自己部署 de Voorhoede 网站,因为我们希望能够控制服务器环境。我们也想要尝试,是否可以通过改变服务端的配置来大大提升性能。当前我们使用了 Apache 服务和 HTTPS 协议。

关键CSS

关键CSS就是阻塞浏览器渲染出用户可识别的网页的一小部分CSS。我们注意网页的 上半版版面。很明显,两个设备的版面的位置有很大的区别。因此,我们做了一个大胆的猜测。

手动地检测这部分关键性的CSS是个很耗时的过程,尤其是样式、特性等不断改变时。这里有几个好的脚本,可以在你构建网页时,生成关键性CSS。我们采用了 Addy Osmani 的版本。

下面是我们分别用关键性CSS和整份CSS分别渲染的页面效果。注意到下半版仍然有部分内容还没有样式。

澳门新浦京娱乐场网站 22

左侧网页是用关键CSS渲染的,而右侧网页则是用整份的CSS。红线是分界线。

配置

为了提升性能和安全性,我们研究了如何配置服务端。

我们使用 H5BP apache样板配置,这个对于改善Apache网络服务的性能和安全性是个很好的开始。他们也有其他服务器环境的配置。对于我们的大部分 HTML、CSS 和 JS,我们使用GZIP压缩。对于我们的所有网站资源,都使用缓存HTTP标头的做法。有兴趣请阅读文件层级缓存的章节。

服务端

我们自己部署 de Voorhoede 网站,因为我们希望能够控制服务器环境。我们也想要尝试,是否可以通过改变服务端的配置来大大提升性能。当前我们使用了 Apache 服务和 HTTPS 协议。

HTTPS

用HTTPS来服务你的网站会对性能造成影响。这主要是设置了SSL握手,引入了大量潜在的东西。但通常情况下,我们可以做一些改变!

HTTP Strict Transport Security 是一个HTTP标头,可以让服务器告诉浏览器只能用HTTPS来与它交互。这种方式防止HTTP请求被重定向为HTTPS。所有尝试用HTTP来访问站点的请求都会被自动转换成HTTPS。这样就节省了一个来回。

TLS false start 允许客户端在第一个TLS回合结束后,马上向后端发送加密的数据。这种优化为一个新的TLS连接减少了握手次数。一旦客户端知道了密钥,就可以开始传输应用数据。剩下的握手用来确认是否有人篡改了握手记录,并且可以并行处理。

TLS session resumption 通过确认浏览器和服务器是否已经取得联系来帮我们节省一个来回。浏览器会记住这一次的标识符,下次发起连接时,这个标识符就会被重用,节省了一个来回。

我听起来像是一个搞开发和运维的,但确实不是。我只是读过一些书,看过一些视频。我喜欢 Google I/O 2016 的 Mythbusting HTTPSEmily Stark 的安全性都市传奇。

配置

为了提升性能和安全性,我们研究了如何配置服务端。

我们使用 H5BP apache样板配置,这个对于改善Apache网络服务的性能和安全性是个很好的开始。他们也有其他服务器环境的配置。对于我们的大部分 HTML、CSS 和 JS,我们使用GZIP压缩。对于我们的所有网站资源,都使用缓存HTTP标头的做法。有兴趣请阅读文件层级缓存的章节。

cookies的使用

我们没有用一门服务端的语言,只有静态的 Apache 网络服务。但一个 Apache 网络服务仍可以做包括SSI在内的后端服务,并且读取cookies。通过巧用cookies和运行那部分被Apache改写的HTML,我们可以大大提升前端性能。下面这个例子就是了(我们实际的代码比这个复杂点,但是思想上是一致的):

XHTML

<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css' )"--> <noscript><link rel="stylesheet" href="0d82f.css"></noscript> <script> (function() { function loadCSS(url) {...} function onloadCSS(stylesheet, callback) {...} function setCookie(name, value, expInDays) {...} var stylesheet = loadCSS('0d82f.css'); onloadCSS(stylesheet, function() { setCookie('css-loaded', '0d82f', 100); }); }()); </script> <style>/* Critical CSS here */</style> <!-- #else --> <link rel="stylesheet" href="0d82f.css"> <!-- #endif -->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css' )"-->
 
<noscript><link rel="stylesheet" href="0d82f.css"></noscript>
<script>
(function() {
    function loadCSS(url) {...}
    function onloadCSS(stylesheet, callback) {...}
    function setCookie(name, value, expInDays) {...}
 
    var stylesheet = loadCSS('0d82f.css');
    onloadCSS(stylesheet, function() {
        setCookie('css-loaded', '0d82f', 100);
    });
}());
</script>
 
<style>/* Critical CSS here */</style>
 
<!-- #else -->
<link rel="stylesheet" href="0d82f.css">
<!-- #endif -->

Apache 服务端逻辑看起来像一行一行的备注,一般以<!-- #开始。我们一步一步来看下吧:

$HTTP_COOKIE!=/css-loaded/ 检测是否存在CSS缓存cookie。 $HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css' 检测缓存的版本是不是当前所要的版本。

If <!-- #if expr="..." --> 如果是true的话,我们就假定这是用户的第一次浏览。

第一次浏览我们添加了 <noscript> 标签,里面放置了 <link rel="stylesheet">。之所以这样做,是因为我们要异步加载整份CSS和JS。如果JS不能用的话,这种做法是不能执行的。这意味着,我们要用常规的加载CSS的方法来做回退。

我们添加了一个行内的脚本来懒加载CSS,onloadCSS 回调里面可以设置cookies.

在同一份脚本里面,我们异步加载了整份CSS。

onloadCSS的回调里面,我们用版本号来设置cookie的值。

在这个脚本后面,我们添加了一行关键CSS的样式。这个会阻塞渲染,但时间是非常短的,而且可以避免页面展示出来只有纯粹的HTML而没有样式的情况。

<!-- #else --> 声明(意味着 css-loaded 的cookie 已经存在)用户重复浏览。因为我们可以从某种程度上来假定,CSS文件之前已经被加载过了,我们可以利用浏览器缓存来提供样式表。这样从缓存里面加载速度就很快了。同样的方法也被用来在第一次异步加载字体,后续的重复浏览也是从缓存里面获取字体。

澳门新浦京娱乐场网站 23

这就是我们第一次和重复浏览时,我们用来区分的cookies。

HTTPS

用HTTPS来服务你的网站会对性能造成影响。这主要是设置了SSL握手,引入了大量潜在的东西。但通常情况下,我们可以做一些改变!

HTTP Strict Transport Security 是一个HTTP标头,可以让服务器告诉浏览器只能用HTTPS来与它交互。这种方式防止HTTP请求被重定向为HTTPS。所有尝试用HTTP来访问站点的请求都会被自动转换成HTTPS。这样就节省了一个来回。

TLS false start 允许客户端在第一个TLS回合结束后,马上向后端发送加密的数据。这种优化为一个新的TLS连接减少了握手次数。一旦客户端知道了密钥,就可以开始传输应用数据。剩下的握手用来确认是否有人篡改了握手记录,并且可以并行处理。

TLS session resumption 通过确认浏览器和服务器是否已经取得联系来帮我们节省一个来回。浏览器会记住这一次的标识符,下次发起连接时,这个标识符就会被重用,节省了一个来回。

我听起来像是一个搞开发和运维的,但确实不是。我只是读过一些书,看过一些视频。我喜欢 Google I/O 2016 的 Mythbusting HTTPSEmily Stark 的安全性都市传奇。

文件级缓存

由于我们在重现页面时很大程度上依赖于浏览器缓存,所以我们需要确认我们的缓存是否合理。理想中我们是要永远的存储资源(CSS、js、 字体、图片),只有当这些文件被修改时才需要更新。当请求的URL是唯一时,缓存就会失效。每更新一个版本,我们都会用git tag 打个标签。所以最简单的方式就是给我们请求的URL加上一个参数(代码版本号),如 https://www.voorhoede.nl/assets/css/main-8af99277a6.css?v=1.0.4.

但是,这种做法的缺点就是当我们要写一个新的博客post(这也是我们代码库的一部分,并没有永久地存储在CMS),原来缓存的资源将会失效,尽管没有改变原来那些资源。

就在我们尝试去改善这种方法时,我们发现了 gulp-revgulp-rev-replace 。这些脚本会自动合理地在我们的文件名称后面添加一窜hash值。这意味着只有实际上文件被改变时,才会去改变请求的URL,这样每个文件的缓存就会自动失效。这种做法让我兴奋不已啊!

cookies的使用

我们没有用一门服务端的语言,只有静态的 Apache 网络服务。但一个 Apache 网络服务仍可以做包括SSI在内的后端服务,并且读取cookies。通过巧用cookies和运行那部分被Apache改写的HTML,我们可以大大提升前端性能。下面这个例子就是了(我们实际的代码比这个复杂点,但是思想上是一致的):

<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;] );?.*/ && ${1} != '0d82f.css' )"-->

<noscript><link rel="stylesheet" href="0d82f.css"></noscript>
<script>
(function() {
    function loadCSS(url) {...}
    function onloadCSS(stylesheet, callback) {...}
    function setCookie(name, value, expInDays) {...}

    var stylesheet = loadCSS('0d82f.css');
    onloadCSS(stylesheet, function() {
        setCookie('css-loaded', '0d82f', 100);
    });
}());
</script>

<style>/* Critical CSS here */</style>

<!-- #else -->
<link rel="stylesheet" href="0d82f.css">
<!-- #endif -->

第一次浏览我们添加了 <noscript> 标签,里面放置了 <link rel="stylesheet">。之所以这样做,是因为我们要异步加载整份CSS和JS。如果JS不能用的话,这种做法是不能执行的。这意味着,我们要用常规的加载CSS的方法来做回退。

我们添加了一个行内的脚本来懒加载CSS,onloadCSS 回调里面可以设置cookies.

在同一份脚本里面,我们异步加载了整份CSS。

onloadCSS的回调里面,我们用版本号来设置cookie的值。

在这个脚本后面,我们添加了一行关键CSS的样式。这个会阻塞渲染,但时间是非常短的,而且可以避免页面展示出来只有纯粹的HTML而没有样式的情况。

`` 声明(意味着 css-loaded 的cookie 已经存在)用户重复浏览。因为我们可以从某种程度上来假定,CSS文件之前已经被加载过了,我们可以利用浏览器缓存来提供样式表。这样从缓存里面加载速度就很快了。同样的方法也被用来在第一次异步加载字体,后续的重复浏览也是从缓存里面获取字体。

澳门新浦京娱乐场网站 24

这就是我们第一次和重复浏览时,我们用来区分的cookies。

结果

如果你看到这里,你应该是想要知道结果的。测试网页的性能可以采用像 PageSpeed Insights 这样的工具,它有很实用的提示。也可以使用 WebPagetest来测试,有扩展性的网络分析。我认为测试网页渲染性能的最好方法就是在疯狂地遏制网络通信时来观察网页的进程。这意味着,用一种不切实际的方法来遏制通信。在谷歌浏览器,你可以这样操作 (via the inspector > Network tab) 来限制通信,观察网页形成过程中,请求是如何缓慢加载的。

下面是我们的网页在 50KB/s 的情况下的加载状况。

澳门新浦京娱乐场网站 25

这是 de Voorhoede site 首屏的网络分析,是网页第一次进程的一个概览。

注意到在50KB/s的网速中,我们是如何让首屏的渲染只用了 2.27 秒的。也就是第一张幻灯片和瀑布图里面的黄色线所代表的位置。黄线恰好绘在就是HTML已经加载完的时间位置。HTML包含了关键CSS,保证页面的可观性。所有其他的CSS都是用懒加载,所以我们可以等到全部资源加载完时才与页面进行交互。这也是我们想要的效果!

另一个值得注意的就是自定义字体从来不在这缓慢的链接上加载。 font face 观察器会自动注意到这一点。但是,如果我们不异步加载字体,你注视大多数浏览器,都会出现FOIT(不可见文本的闪现,上文有提及)的情况。

所有的CSS文件仅在8s后就都加载完毕。相反,如果我们不采用加载关键CSS的方式,而是采用加载全部CSS,我们在前8秒看到的将会是空白的页面。

如果你感到好奇,想与那些不太注重性能的网站对比一下加载时间,那赶紧试试吧。那个时间肯定是飞涨啊!

用上面介绍的工具测试了我们的网站,结果也是让人满意的。 PageSpeed insights 在移动性能方面给了我们100分,多么了不起啊!

PageSpeed insightsvoorhoede.nl的测试结果! 速度100分!

当我们查看 WebPagetest 时,我们得到下面这样的结果:

澳门新浦京娱乐场网站 26WebPagetest 对 voorhoede.nl的检测结果

可以看出,我们的服务器运行良好,首屏的速度指标是693。 这意味着我们的页面在693毫秒后就可以在宽屏缆线下被使用了。

文件级缓存

由于我们在重现页面时很大程度上依赖于浏览器缓存,所以我们需要确认我们的缓存是否合理。理想中我们是要永远的存储资源(CSS、js、 字体、图片),只有当这些文件被修改时才需要更新。当请求的URL是唯一时,缓存就会失效。每更新一个版本,我们都会用 git tag 打个标签。所以最简单的方式就是给我们请求的URL加上一个参数(代码版本号),如 https://www.voorhoede.nl/assets/css/main-8af99277a6.css?v=1.0.4.

但是,这种做法的缺点就是当我们要写一个新的博客post(这也是我们代码库的一部分,并没有永久地存储在CMS),原来缓存的资源将会失效,尽管没有改变原来那些资源。

就在我们尝试去改善这种方法时,我们发现了 gulp-revgulp-rev-replace 。这些脚本会自动合理地在我们的文件名称后面添加一窜hash值。这意味着只有实际上文件被改变时,才会去改变请求的URL,这样每个文件的缓存就会自动失效。这种做法让我兴奋不已啊!

技术路线

我们这样还不算完成,还会不断地重复我们的方法。我不久的将来,我们会主要关注以下内容:

结果

如果你看到这里,你应该是想要知道结果的。测试网页的性能可以采用像 PageSpeed Insights 这样的工具,它有很实用的提示。也可以使用 WebPagetest来测试,有扩展性的网络分析。我认为测试网页渲染性能的最好方法就是在疯狂地遏制网络通信时来观察网页的进程。这意味着,用一种不切实际的方法来遏制通信。在谷歌浏览器,你可以这样操作 (via the inspector > Network tab) 来限制通信,观察网页形成过程中,请求是如何缓慢加载的。

下面是我们的网页在 50KB/s 的情况下的加载状况。

澳门新浦京娱乐场网站 27

这是 de Voorhoede site 首屏的网络分析,是网页第一次进程的一个概览。

注意到在50KB/s的网速中,我们是如何让首屏的渲染只用了 2.27 秒的。也就是第一张幻灯片和瀑布图里面的黄色线所代表的位置。黄线恰好绘在就是HTML已经加载完的时间位置。HTML包含了关键CSS,保证页面的可观性。所有其他的CSS都是用懒加载,所以我们可以等到全部资源加载完时才与页面进行交互。这也是我们想要的效果!

另一个值得注意的就是自定义字体从来不在这缓慢的链接上加载。 font face 观察器会自动注意到这一点。但是,如果我们不异步加载字体,你注视大多数浏览器,都会出现 FOIT(不可见文本的闪现,上文有提及)的情况。

所有的CSS文件仅在8s后就都加载完毕。相反,如果我们不采用加载关键CSS的方式,而是采用加载全部CSS,我们在前8秒看到的将会是空白的页面。

如果你感到好奇,想与那些不太注重性能的网站对比一下加载时间,那赶紧试试吧。那个时间肯定是飞涨啊!

用上面介绍的工具测试了我们的网站,结果也是让人满意的。 PageSpeed insights 在移动性能方面给了我们100分,多么了不起啊!

PageSpeed insightsvoorhoede.nl的测试结果! 速度100分!

当我们查看 WebPagetest 时,我们得到下面这样的结果:

澳门新浦京娱乐场网站 28

WebPagetest 对 voorhoede.nl的检测结果

可以看出,我们的服务器运行良好,首屏的速度指标是693。 这意味着我们的页面在693毫秒后就可以在宽屏缆线下被使用了。

HTTP/2

目前我们正在试验HTTP/2。本文所描述的大多数东西都是基于 HTTP/1.1 权限内的最好实践。简言之,HTTP/1.1 要追述到1999年,那时 table布局和行内样式都如火如荼。HTTP/1.1 从没为 2.6MB的网页要接受200个请求而设计。为了减轻旧版协议带来的痛楚,我们结合JS、CSS、关键性CSS,还为小图片设置数据源URI等。用各种方法来节省请求。自从 HTTP/2 可以在同一个TCP链接上平行地运行多个请求后,所有的这些联结使用和减少请求的做法都可能成为反面模式了。当我们跑完这个实验后,我们将会采用 HTTP/2 协议。

技术路线

我们这样还不算完成,还会不断地重复我们的方法。我不久的将来,我们会主要关注以下内容:

Service Workers

这是一个在后台运行的现代浏览器的 JavaScript API。它拥有很多特性,这些特性在以前的网站上都是没有的,如离线支持、消息推送、背景同步等。我们现在正尝试用 Service Worker, 但还是得在我们自己的网站上实现先。我向你保证,我们会做到的!

HTTP/2

目前我们正在试验HTTP/2。本文所描述的大多数东西都是基于 HTTP/1.1 权限内的最好实践。简言之,HTTP/1.1 要追述到1999年,那时 table布局和行内样式都如火如荼。HTTP/1.1 从没为 2.6MB的网页要接受200个请求而设计。为了减轻旧版协议带来的痛楚,我们结合JS、CSS、关键性CSS,还为小图片设置数据源URI等。用各种方法来节省请求。自从 HTTP/2 可以在同一个TCP链接上平行地运行多个请求后,所有的这些联结使用和减少请求的做法都可能成为反面模式了。当我们跑完这个实验后,我们将会采用 HTTP/2 协议。

CDN

因此,我们想要自己控制和部署我们的网站。而且现在我们也要采用CDN来摆脱由服务端和客户端实际距离所带来的网络问题。尽管我们的用户基本上都是荷兰的,我们也想向世界的前端社区反映我们在质量、性能和推动网络发展方面做的最好。

1 赞 9 收藏 评论

澳门新浦京娱乐场网站 29

Service Workers

这是一个在后台运行的现代浏览器的 JavaScript API。它拥有很多特性,这些特性在以前的网站上都是没有的,如离线支持、消息推送、背景同步等。我们现在正尝试用 Service Worker, 但还是得在我们自己的网站上实现先。我向你保证,我们会做到的!

CDN

因此,我们想要自己控制和部署我们的网站。而且现在我们也要采用CDN来摆脱由服务端和客户端实际距离所带来的网络问题。尽管我们的用户基本上都是荷兰的,我们也想向世界的前端社区反映我们在质量、性能和推动网络发展方面做的最好。

本文由澳门新浦京娱乐场网站发布于新浦京娱乐场官网,转载请注明出处:澳门新浦京娱乐场网站:从案例剖析哪些优化前